Charts with multiple lines can be constructed in 2 ways-
- Using the
dataSet
prop which is an array of objects, each object defining a line. Each object in thedataSet
array expects a mndatory property nameddata
which is an array of numeric values. - Using a separate prop like
data
,data2
,data3
,data4
,data5
for each line. A max of 5 lines can be drawn in this way. Another line corresponding to the secondary Y-axis (right-side Y-axis) can be drawn using thesecondaryData
prop.
The lines or curves are rendered in the lineSvgComponent
which renders a <Svg>
. The flow from <LineChart>
to lineSvgComponent
is-
LineChart -> BarAndLineChartsWrapper -> renderChartContent -> renderLine -> lineSvgComponent
Here's a better visualisation with conditions and loops-
LineChart() {
BarAndLineChartsWrapper() {
renderChartContent() {
if (dataSet) {
dataSet.forEach(set => {
if (animated) {
renderAnimatedLine(set) -> lineSvgComponent()
}
else {
renderLine(set) -> lineSvgComponent()
}
})
}
else {
if (animated) {
renderAnimatedLine(data) -> lineSvgComponent()
renderAnimatedLine(data2) -> lineSvgComponent()
.
.
renderAnimatedLine(data5) -> lineSvgComponent()
renderAnimatedLine(secondaryData) -> lineSvgComponent()
}
else {
renderLine(data) -> lineSvgComponent()
renderLine(data2) -> lineSvgComponent()
.
.
renderLine(data5) -> lineSvgComponent()
renderLine(secondaryData) -> lineSvgComponent()
}
}
// Pointer is rendered at last, after all the lines/curves have been rendered.
// This ensures that the pointer appears on top of all UI elements in the chart
renderPointer()
}
}
}
If there are n
lines, the lineSvgComponent
ic called n
times. This means n <Svg>
components will be rendered on top of one another making a stack.
Due to this the topmost (last) line will block the other lines. Since the <Svg>
for each line has a transparent background, there will be no problem in visibility.
But the onPress
events will work only for the last line and fail for the others. This problem is solved by rendering the data points separately as discussed below-
To solve the aforementioned onPress
issue for multi-line charts, we are rendering the data points separately.
The data points will be rendered after all the lines have been rendered. Thus all the data points will be on top of the topmost <Svg>
.
This is implemented in code in the following way-
We have a variable named lastLineNumber
which stores the total number of lines the currently rendered chart has. So, if we are using dataSet
which has 10 items, then lastLineNumber
will be 10. If we are using data
and data2
then the lastLineNumber
will be 2. For secondaryData
we have a special value of lastLineNumber
that is- 6667
. This large number is chosen to ensure that the secondary data is rendered at last.
In the lineSvgComponent
we check if the currently rendered line is the last line, then after rendering that line, we render all the data points for the current line as well as all the previously rendered lines using the function named renderDataPointsForEachLine
. This solves the onPress
issue.
But if the chart is animated, and if we render the data points only after all the lines have been rendered, then it may not be a great UI experience. Usually we would like to see the data points appear along with the lines/curves.
To handle this we are rendering the data points separately only for non-animated line charts. For animated charts we render the data points separately only if the renderDataPointsAfterAnimationEnds
prop is set to true.