'react-chartjs-2 vertical line when hovering over chart
I'm trying to create a linechart, using react-chartjs-2, that has a vertical line when hovering over the datapoints in the chart. Like in this picture below:
I tried using the chartjs-plugin-annotation plugin, but with mixed results. I managed to create a static line, not understanding how or why it works. How should I achieve this? Am I onto something?
const data = {
labels: [...week(d)],
datasets: [
{
...
data: [10000, 9500, 7000, 4500, 2500, 1500, 500, 0],
}
]
};
var line = [{
type: "line",
mode: "vertical",
// ???
scaleID: "y-axis-0",
value: -20000,
borderColor: "#2984c5",
borderWidth: 1,
}];
const options = {
tooltips: {
xPadding: 20,
yPadding: 10,
displayColors: false,
bodyFontSize: 16,
bodyFontStyle: 'bold',
},
annotation: {
annotations: line,
},
scales: {
yAxes: [{
gridLines: {
drawBorder: false,
tickMarkLength: 0,
},
ticks: {
fontSize: 14,
padding: 15,
max: data.maxY,
min: 0,
maxTicksLimit: 6,
fontColor: "#485465"
}
}],
xAxes: [{
ticks: {
padding: 5,
fontSize: 14,
fontColor: "#485465",
},
gridLines: {
display: false,
},
},
],
},
responsive: false,
}
I have my full code available here: https://codesandbox.io/s/y28mk3rn4z
Solution 1:[1]
The below answer is correct it only needs some tweaks, Thanks Jordan.
Chart.pluginService.register({
afterDraw: function(chart, easing) {
if (chart.tooltip._active && chart.tooltip._active.length) {
const activePoint = chart.controller.tooltip._active[0];
const ctx = chart.ctx;
const x = activePoint.tooltipPosition().x;
const topY = chart.scales['y-axis-0'].top;
const bottomY = chart.scales['y-axis-0'].bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#e23fa9';
ctx.stroke();
ctx.restore();
}
}
});
In the chart options we need to add the below config to display the line on near by hover.
tooltips: {
mode: 'index',
intersect: false
},
hover: {
mode: 'index',
intersect: false
}
PS: if there are multiple chart plotted together then peace of code might cause problems. if we want to have this effect on a specific chart(e.g line) then we can add the below condition.
if (
chart.tooltip._active &&
chart.tooltip._active.length &&
chart.config.type === 'line'
)
This worked for me, hope this help.
Solution 2:[2]
Just looked into this exact thing and this will get you there! It won't do the fills on either side of the line, but it creates the vertical line!
componentWillMount() {
Chart.pluginService.register({
afterDraw: function (chart, easing) {
if (chart.tooltip._active && chart.tooltip._active.length) {
const activePoint = chart.controller.tooltip._active[0];
const ctx = chart.ctx;
const x = activePoint.tooltipPosition().x;
const topY = chart.scales['y-axis-1'].top;
const bottomY = chart.scales['y-axis-1'].bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#e23fa9';
ctx.stroke();
ctx.restore();
}
}
});
}
Also for anyone with multiple datasets you can add this into your options for tooltips on all lines at once.
tooltips: {
mode: 'x'
},
Solution 3:[3]
In case someone needs it for "react-chartjs-2": "^4.0.1".
This one worked for me based on previous answers:
import { Chart } from 'chart.js';
import { Line } from 'react-chartjs-2';
Chart.register(
{
id: 'uniqueid5', //typescript crashes without id
afterDraw: function (chart: any, easing: any) {
if (chart.tooltip._active && chart.tooltip._active.length) {
const activePoint = chart.tooltip._active[0];
const ctx = chart.ctx;
const x = activePoint.element.x;
const topY = chart.scales.y.top;
const bottomY = chart.scales.y.bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#e23fa9';
ctx.stroke();
ctx.restore();
}
}
}
);
.
.
.
<Line
options={{
...options,
interaction: {
mode: 'index',
intersect: false,
}}}
data={data}
/>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Jordan Duncan |
Solution 3 | David Rios |