The visx library by airbnb is my to-go-to when building charts with React.
It’s well documented, based on d3, really flexible and customizable.
You can find a lot of articles explaining to you how to build a simple chart using visx, they even provide some sandbox.
When struggling with a chart, always go to the d3 documentation and do some research on how to perform this or that.
How can I align properly two charts of different types and scales?
I need to display some data over time, my x-axis is time-based in this example.
The stack bar chart needs by default a scale band.
I would like to add a line displaying other data based on the same x-axis values I’m using for the bar stack chart.
❌ Create two different x-axis scales
My first approach was to create two different x-axis scales. But because the line chart and the bar stack are not using the same scale type, the alignment of my two charts was off.
✅ Only use one scale type
So as a requirement, I could only use one scale type, since I don’t have much choice with the bar stack, it had to be a scale band.
import { scaleBand, scaleLinear, scaleOrdinal } from '@visx/scale';
...
// create the scale that will be passed to the <BarStack /> component
const xScale = scaleBand({
domain: selectedBarPoints.map(getX),
});
In order to get the line chart aligned, I need to use my xScale
somehow.
The key is to pass the xScale
that we are using for the bar stack chart to the x
props of the LinePath
, but with a twist:
x={d => xScale.bandwidth() / 2 + xScale(d.x)}
Explanations
// width of the bar divided by 2 to get the middle
xScale.bandwidth() / 2
// the x position of the bar in the chart
xScale(d.x)
Here is a more "in-context" example, also adding dots on top of the line:
<svg ref={containerRef} width="100%" height={height}>
<Group
width={width}
height={height}
top={margin.top}
left={margin.left}
>
<BarStack
data={selectedBarPoints}
x={getX}
xScale={xScale}
yScale={yScale}
>
{barStacks =>
barStacks.map(barStack =>
map(barStack.bars, (bar, index) =>
(
<rect
x={bar.x}
y={bar.y - 0.8}
height={bar.height}
width={bar.width}
/>
)),
)
}
</BarStack>
{selectedLinePoints.map((datapoints, i) => (
<Fragment key={`fragment-line-${datapoints.id}`}>
<LinePath
data={datapoints.data}
// here is where the magic happens
x={d => xScale.bandwidth() / 2 + xScale(d.x)}
y={d => yScale(d.y)}
/>
{datapoints.data.map((dot) => (
<g key={`line-glyph-${dot.x}`}>
<GlyphCircle
// operate the same logic to position a dot on the line
left={xScale.bandwidth() / 2 + xScale(dot.x)}
top={yScale(dot.y)}
/>
</g>
))}
</Fragment>
))}
</Group>
</svg>
Happy coding charts!
Top comments (0)