Line charts
This tutorial is a variation around the general introduction to line chart with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to create several line charts and add asynchronized cursor on all of them. Hovering over one graph will display a cursor on all of them, easing the understanding of synchronized patterns.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual line chart.
Plot and code
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
Two line charts are displayed one next to each other. It shows the evolution of 2 numeric valriables.
Hovering over a chart will display a cursor that is synced with the other chart. This helps finding a relationship between them.
Hover over a chart to see a cursor on both of them, easing the time comparison.
The Data
Two dataset are used here. Both with the same format as described in theline chart section of the gallery.
Here is a minimal example of the data structure:
const data = [
{x:1, y: 90},
{x: 2, y: 12},
{x: 3, , y: 34},
{x: 4, , y: 53},
{x: 5, , y: 98},
]
Shared state
The parent component calling the 2 LineChart
components stores a shared state. This state can be created as follow using the react useState hook:
const [cursorPosition, setCursorPosition] = useState<number | null>(null);
cursorPosition
will be null
if no charts is hovered over, and a number
that provides a position in pixel otherwise. setCursorPosition
is a setter: a function that updates the value of cursorPosition
Both cursorPosition
and setCursorPosition
must be passed to the line chart component as props.
Trigger state update
When the mouse moves over the chart area, the state must be updated using setCursorPosition
To do so, a SVG rect
element is added on top of the chart area. It will catch the onMouseMove
events. This rect
looks like:
<rect
x={0}
y={0}
width={boundsWidth}
height={boundsHeight}
onMouseMove={onMouseMove}
onMouseLeave={() => setCursorPosition(null)}
visibility={"hidden"}
pointerEvents={"all"}
/>
There are several strategies available when it comes to write the onMouseMove
function. Here I suggest to
- use
getBoundingClientRect()
to find the position of the mouse in the chart area - find the closest data point in the dataset. This requires the
invert
function of the d3 linear scale - update the state after using the linear scale again
const onMouseMove = (e: React.MouseEvent<SVGRectElement>) => {
const rect = e.currentTarget.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const closest = getClosestPoint(mouseX);
setCursorPosition(xScale(closest.x));
};
Animated cursor
I used react spring to animate the cursor transition. When the mouse moves, the cursor position does not change instantly but moves smoothly to the new position.
Animation in dataviz using React is a big topic. It's impossible to go in-depth here! I will publish a dedicated blog post on the topic soon. Please subscribe to the newsletter if you want to be notified.
Hover over a chart to see a cursor on both of them, easing the time comparison.
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data
with gmail.com
. You can also subscribe to the newsletter to know when I publish more content!