chartjs display crosshair onHover - chart.js

https://codepen.io/qkreltms/pen/BaaWZeV?editors=0010
I've tried making crosshair when hover on points:
But there are some bugs
Crosshair appears when mouse hover on the almost exactly middle on the points, not any position on points.
Crosshair border keeps thicker when mouse points moves middle on the points.
Any ideas?
Code :
var ctx = document.getElementById("myChart").getContext("2d");
const colors = {
darkBlue: {
fill: '#92bed2',
stroke: '#3282bf',
},
purple: {
fill: '#8fa8c8',
stroke: '#75539e',
}
};
const test = [5, 9, 10, 9, 18, 19, 20];
const myChart = new Chart(ctx, {
type: 'line',
data: {
labels: test,
datasets: [{
label: "Unavailable",
fill: true,
backgroundColor: colors.purple.fill,
pointBackgroundColor: colors.purple.stroke,
borderColor: colors.purple.stroke,
pointHighlightStroke: colors.purple.stroke,
borderCapStyle: 'butt',
data: test,
}]
},
options: {
onHover: function(event) {
const chart = myChart.getElementAtEvent(event)[0]._chart;
const activeElements = myChart.getElementsAtEvent(event);
console.log(chart)
if (activeElements.length) {
const activePoint = activeElements[0];
const ctx = chart.ctx;
if (!ctx) {
return;
}
const x = activePoint._view.x;
const y = activePoint._view.y;
const leftX = chart.chartArea.left;
const topY = chart.chartArea.top;
const RightX = chart.chartArea.right;
const bottomY = chart.chartArea.bottom;
ctx.beginPath();
// ctx.setLineDash([5, 15]);
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.moveTo(leftX, y);
ctx.lineTo(RightX, y);
ctx.lineWidth = 1;
ctx.strokeStyle = "#C2C7CC";
ctx.stroke();
ctx.closePath();
}
},
responsive: false,
scales: {
},
animation: {
duration: 750,
},
}
});

Related

Add line from point to x-axis and bold label of him

I have a chart.js in my website
When hovering over a point in the graph, I need to draw a line that goes down to the x-axis and bold his label
like this:
[chart][1]
Can someone help me?
You will need a custom plugin for this, see live example:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'red',
backgroundColor: 'red'
}]
},
options: {
plugins: {
selector: {
color: 'blue',
width: 2,
// If canvas has a different backgroundColor set this to match
boxColor: 'white'
}
}
},
plugins: [{
id: 'selector',
beforeDatasetsDraw: (chart, args, opts) => {
// Return if no hover
if (chart._active.length === 0) {
return;
}
// Set all variables needed
const activeEl = chart._active[0].element;
const labelItem = chart.scales.x._labelItems[chart._active[0].index];
const {
ctx,
scales: {
y
}
} = chart;
const metrics = ctx.measureText(labelItem.label);
const labelWidth = metrics.width;
const labelHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
// Draw white box over old label
ctx.save();
ctx.fillStyle = opts.boxColor || '#FFFFFF';
ctx.fillRect((labelItem.translation[0] - labelWidth / 2), labelItem.translation[1], labelWidth, labelHeight);
ctx.restore();
// Draw new text on canvas
ctx.save();
ctx.font = Chart.helpers.toFontString(Object.assign(labelItem.font, {
style: 'bold'
}));
ctx.fillText(labelItem.label, (labelItem.translation[0] - labelWidth / 2), labelItem.translation[1] + labelItem.textOffset)
ctx.restore();
// Draw vertical line down from point
ctx.save();
ctx.lineWidth = opts.width || 1;
ctx.strokeStyle = opts.color || 'black';
ctx.beginPath();
ctx.moveTo(activeEl.x, activeEl.y);
ctx.lineTo(activeEl.x, y.getPixelForValue(y.min));
ctx.stroke();
ctx.restore();
}
}]
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.4.0/chart.js"></script>
</body>

Chart.js Tooltip over the line not only over points

I have a line chart in chart.js. Now I would like to have a tooltip, when I hover over the line not just when i hover over a point.
The tooltip should then be displayed at the position of the mouse.
Is this somehow possible?
I have tried all interaction.mode options, but non of them have this behavior.
Thank You
You can use the croshair plugin for this if you are still using v2 of the lib: https://chartjs-plugin-crosshair.netlify.app/
Live example:
var ctx = document.getElementById("myChart");
function generateDataset(shift, label, color) {
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
let data = [];
let index = 0;
while (index < 5) {
data.push({
x: index,
y: getRandomInt(10, 40)
});
index++;
}
var dataset = {
backgroundColor: color,
borderColor: color,
showLine: true,
fill: false,
pointRadius: 2,
label: label,
data: data,
lineTension: 0,
interpolate: true,
xAxisID: 'x-axis-0'
};
return dataset;
}
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D', 'E'],
datasets: [
generateDataset(0, "A", "red"),
generateDataset(1, "B", "green")
]
},
options: {
scales: {
xAxes: [{
display: true,
type: 'linear',
position: 'bottom',
id: 'x-axis-0',
ticks: {
source: 'labels'
}
}]
},
tooltips: {
mode: "interpolate",
intersect: false,
callbacks: {
title: function(a, d) {
// return a[0].xLabel.toFixed(2);
return a[0].xLabel;
},
label: function(i, d) {
return (
d.datasets[i.datasetIndex].label + ": " + i.yLabel.toFixed(2)
);
}
}
},
plugins: {
crosshair: {
line: {
// Add alpha chanel so line becomes transparant so you dont see it
color: '#ffffff00',
},
sync: {
enabled: false
}
}
}
}
});
.myChartDiv {
max-width: 600px;
max-height: 400px;
}
<html>
<body>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"></canvas>
</div>
<script src="https://npmcdn.com/chart.js#2.9.4/dist/Chart.bundle.min.js"></script>
<script src="https://unpkg.com/chartjs-plugin-crosshair#1.1.6/dist/chartjs-plugin-crosshair.js"></script>
</body>
</html>
If you are using V3 of the lib you will need to use a custom own plugin
Example:
// Options for the indicators
const indicatorOptions = {
radius: 4, borderWidth: 1, borderColor: 'red', backgroundColor: 'transparent'
};
// Override getLabelAndValue to return the interpolated value
const getLabelAndValue = Chart.controllers.line.prototype.getLabelAndValue;
Chart.controllers.line.prototype.getLabelAndValue = function(index) {
if (index === -1) {
const meta = this.getMeta();
const pt = meta._pt;
const vScale = meta.vScale;
return {
label: 'interpolated',
value: vScale.getValueForPixel(pt.y)
};
}
return getLabelAndValue.call(this, index);
}
// The interaction mode
Chart.Interaction.modes.interpolate = function (chart, e, option) {
const x = e.x;
const items = [];
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
const meta = metas[i];
const pt = meta.dataset.interpolate({ x }, "x");
if (pt) {
const element = new Chart.elements.PointElement({...pt, options: {...indicatorOptions}});
meta._pt = element;
items.push({element, index: -1, datasetIndex: meta.index });
} else {
meta._pt = null;
}
}
return items;
};
// Plugin to draw the indicators
Chart.register({
id: 'indicators',
afterDraw(chart) {
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
const meta = metas[i];
if (meta._pt) {
meta._pt.draw(chart.ctx);
}
}
},
afterEvent(chart, args) {
if (args.event.type === 'mouseout') {
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
metas[i]._pt = null;
}
args.changed = true;
}
}
})
var ctx = document.getElementById("myChart").getContext("2d");
var chart = new Chart(ctx, {
type: "line",
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
fill: true,
label: "My First dataset",
backgroundColor: "rgba(132, 0, 0, 1)",
borderColor: "rgb(255, 99, 132)",
data: [0, 10, 5, 2, 20, 30, 45]
},
{
data: [30, 40, 50],
label: 'My Second Dataset',
fill: true,
backgroundColor: "lightgreen",
borderColor: "green"
}
]
},
options: {
interaction: {
mode: "interpolate",
intersect: false,
axis: "x"
},
plugins: {
tooltip: {
position: 'nearest',
displayColors: false,
}
}
},
});
.myChartDiv {
max-width: 600px;
max-height: 400px;
}
<h1>Interpolating line values</h1>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.3.2/chart.js"></script>
</div>

Responsive legend font size in react-chartjs 2

I have tried to make a pie chart using react-chartjs 2, It is working fine in desktop view but in mobile view the legend is not responsive it is taking much space and due to this the size of pie chart become very small.
my code:
function Chart(props: any) {
const option = {
tooltips: {enter image description here
callbacks: {
label: function (tooltipItem: any, data: any) {
var dataset = data.datasets[tooltipItem.datasetIndex];
var meta = dataset._meta[Object.keys(dataset._meta)[0]];
var total = meta.total;
var currentValue = dataset.data[tooltipItem.index];
var percentage = parseFloat(
((currentValue / total) * 100).toFixed(1)
);
return currentValue + " (" + percentage + "%)";
},
title: function (tooltipItem: any, data: any) {
return data.labels[tooltipItem[0].index];
},
},
},
legend: {
display: true,
labels: {
fontSize: 12,
},
position: "right",
},
};
return (
<div className="chart">
<Pie data={props.ChartData} options={option} />
</div>
);
You can set your fontSize object as a ternery operator that checks the widts (or something else) to see if you are on a mobile device and give back the right fontSize according to it
If you want to update it real time because screen sizes change you can do that by mutating the chart options itself in a resizeEvent listner
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
legend: {
labels: {
fontSize: window.innerWidth > 350 ? 20 : 10
}
},
scales: {
yAxes: [{
ticks: {
reverse: false
}
}]
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
const chart = new Chart(ctx, options);
window.addEventListener('resize', () => {
if (window.innerWidth < 350) {
chart.options.legend.labels.fontSize = 10;
} else {
chart.options.legend.labels.fontSize = 20
}
});
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js" integrity="sha512-hZf9Qhp3rlDJBvAKvmiG+goaaKRZA6LKUO35oK6EsM0/kjPK32Yw7URqrq3Q+Nvbbt8Usss+IekL7CRn83dYmw==" crossorigin="anonymous"></script>
</body>
Here is the way to have a responsive legend font size (also explained here)
options: {
plugins: {
legend: {
labels: {
// This more specific font property overrides the global property
font: {
size: 14
}
}
}
}
}

Display Chartjs tooltip values outside the canvas - multi line chart

I am trying to replicate the NSE Stock exchange comparison graph as displayed here.
I am using Chartjs 2.0. I have 2 line graphs in the chart.
On hovering on the data points I want to show the tooltip's ylabel in a div which is outside the canvas, (like the changing values are displayed in the top right corner in the chart above)
I found GRUNT's code helpful
Moving vertical line when hovering over the chart using chart.js
You can use Label Callback
document.getElementById('y-label').textContent = tooltipItem.yLabel;
Chart.defaults.LineWithLine = Chart.defaults.line;
Chart.controllers.LineWithLine = Chart.controllers.line.extend({
draw: function(ease) {
Chart.controllers.line.prototype.draw.call(this, ease);
if (this.chart.tooltip._active && this.chart.tooltip._active.length) {
var activePoint = this.chart.tooltip._active[0],
ctx = this.chart.ctx,
x = activePoint.tooltipPosition().x,
topY = this.chart.scales['y-axis-0'].top,
bottomY = this.chart.scales['y-axis-0'].bottom;
// draw line
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#07C';
ctx.stroke();
ctx.restore();
}
}
});
var chart = new Chart(ctx, {
type: 'LineWithLine',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
datasets: [{
label: 'Statistics',
data: [3, 1, 2, 5, 4, 7, 6],
backgroundColor: 'rgba(0, 119, 204, 0.8)',
borderColor: 'rgba(0, 119, 204, 0.3)',
fill: false
}]
},
options: {
responsive: false,
tooltips: {
intersect: false,
callbacks: {
label: function(tooltipItem, data) {
var label = data.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += Math.round(tooltipItem.yLabel * 100) / 100;
document.getElementById('y-label').textContent = tooltipItem.yLabel;
return label;
}
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<h4 id="y-label"> </h4>
<canvas id="ctx" height="200"></canvas>

Dotted lines using Chart.js

Is it possible to draw dotted lines using Chart.js?
This is an example of what I want to do:
The current options I'm using is not letting me do what I aim to do:
var lineChartData = {
"datasets": {
"label": "defi score",
"data": dataset[i],
"pointStrokeColor": "#fff",
"fillColor": "rgba(220,220,220,0.5)",
"pointColor": "rgba(220,220,220,1)",
"strokeColor": "rgba(220,220,220,1)",
pointHighlightFill: "#19283F",
pointHighlightStroke: "#28AFFA",
bezierCurve: false
},
"labels": labels
};
var ctx = document.getElementById("chart_per_week").getContext("2d");
var myLine = new Chart(ctx).Line(lineChartData, {
responsive: true,
scaleFontColor: "#FF5972",
bezierCurve: false
});
Dotted Line using Chart.js
You can extend the line chart type to do this
Preview
Script
Chart.types.Line.extend({
name: "LineAlt",
initialize: function (data) {
var strokeColors = [];
data.datasets.forEach(function (dataset, i) {
if (dataset.dottedFromLabel) {
strokeColors.push(dataset.strokeColor);
dataset.strokeColor = "rgba(0,0,0,0)"
}
})
Chart.types.Line.prototype.initialize.apply(this, arguments);
var self = this;
data.datasets.forEach(function (dataset, i) {
if (dataset.dottedFromLabel) {
self.datasets[i].dottedFromIndex = data.labels.indexOf(dataset.dottedFromLabel) + 1;
self.datasets[i]._saved = {
strokeColor: strokeColors.shift()
}
}
})
},
draw: function () {
Chart.types.Line.prototype.draw.apply(this, arguments);
// from Chart.js library code
var hasValue = function (item) {
return item.value !== null;
},
nextPoint = function (point, collection, index) {
return Chart.helpers.findNextWhere(collection, hasValue, index) || point;
},
previousPoint = function (point, collection, index) {
return Chart.helpers.findPreviousWhere(collection, hasValue, index) || point;
};
var ctx = this.chart.ctx;
var self = this;
ctx.save();
this.datasets.forEach(function (dataset) {
if (dataset.dottedFromIndex) {
ctx.lineWidth = self.options.datasetStrokeWidth;
ctx.strokeStyle = dataset._saved.strokeColor;
// adapted from Chart.js library code
var pointsWithValues = Chart.helpers.where(dataset.points, hasValue);
Chart.helpers.each(pointsWithValues, function (point, index) {
if (index >= dataset.dottedFromIndex)
ctx.setLineDash([3, 3]);
else
ctx.setLineDash([]);
if (index === 0) {
ctx.moveTo(point.x, point.y);
}
else {
if (self.options.bezierCurve) {
var previous = previousPoint(point, pointsWithValues, index);
ctx.bezierCurveTo(
previous.controlPoints.outer.x,
previous.controlPoints.outer.y,
point.controlPoints.inner.x,
point.controlPoints.inner.y,
point.x,
point.y
);
}
else {
ctx.lineTo(point.x, point.y);
}
}
ctx.stroke();
}, this);
}
})
ctx.restore();
}
});
and then
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
...
dottedFromLabel: "April"
}
],
};
...
new Chart(ctx).LineAlt(data);
Fiddle - https://jsfiddle.net/3gxjfndm/3/
With chart.js V3 you can make use of segments to achieve this:
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'orange',
segment: {
borderDash: ctx => ((ctx.chart.data.datasets[ctx.datasetIndex].data.length - 1) === ctx.p1DataIndex ? [2, 2] : undefined)
}
}]
},
options: {}
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.js"></script>
</body>