How can I draw an vertical line at a particular point on the x-axis using Chart.js?
In particular, I want to draw a line to indicate the current day on a LineChart. Here's a mockup of the chart: https://i.stack.imgur.com/VQDWR.png
How can I draw an vertical line at a particular point on the x-axis using Chart.js?
In particular, I want to draw a line to indicate the current day on a LineChart. Here's a mockup of the chart: https://i.stack.imgur.com/VQDWR.png
Update - this answer is for Chart.js 1.x, if you are looking for a 2.x answer check the comments and other answers.
You extend the line chart and include logic for drawing the line in the draw function.
Preview
HTML
<div>
<canvas id="LineWithLine" width="600" height="400"></canvas>
</div>
Script
var data = {
labels: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
datasets: [{
data: [12, 3, 2, 1, 8, 8, 2, 2, 3, 5, 7, 1]
}]
};
var ctx = document.getElementById("LineWithLine").getContext("2d");
Chart.types.Line.extend({
name: "LineWithLine",
draw: function () {
Chart.types.Line.prototype.draw.apply(this, arguments);
var point = this.datasets[0].points[this.options.lineAtIndex]
var scale = this.scale
// draw line
this.chart.ctx.beginPath();
this.chart.ctx.moveTo(point.x, scale.startPoint + 24);
this.chart.ctx.strokeStyle = '#ff0000';
this.chart.ctx.lineTo(point.x, scale.endPoint);
this.chart.ctx.stroke();
// write TODAY
this.chart.ctx.textAlign = 'center';
this.chart.ctx.fillText("TODAY", point.x, scale.startPoint + 12);
}
});
new Chart(ctx).LineWithLine(data, {
datasetFill : false,
lineAtIndex: 2
});
The option property lineAtIndex controls which point to draw the line at.
Fiddle - http://jsfiddle.net/dbyze2ga/14/
Sharing my solution for chartjs.org version 2.5. I wanted to use a plugin, to make the implementation reusable.
const verticalLinePlugin = {
getLinePosition: function (chart, pointIndex) {
const meta = chart.getDatasetMeta(0); // first dataset is used to discover X coordinate of a point
const data = meta.data;
return data[pointIndex]._model.x;
},
renderVerticalLine: function (chartInstance, pointIndex) {
const lineLeftOffset = this.getLinePosition(chartInstance, pointIndex);
const scale = chartInstance.scales['y-axis-0'];
const context = chartInstance.chart.ctx;
// render vertical line
context.beginPath();
context.strokeStyle = '#ff0000';
context.moveTo(lineLeftOffset, scale.top);
context.lineTo(lineLeftOffset, scale.bottom);
context.stroke();
// write label
context.fillStyle = "#ff0000";
context.textAlign = 'center';
context.fillText('MY TEXT', lineLeftOffset, (scale.bottom - scale.top) / 2 + scale.top);
},
afterDatasetsDraw: function (chart, easing) {
if (chart.config.lineAtIndex) {
chart.config.lineAtIndex.forEach(pointIndex => this.renderVerticalLine(chart, pointIndex));
}
}
};
Chart.plugins.register(verticalLinePlugin);
Usage is simple then:
new Chart(ctx, {
type: 'line',
data: data,
label: 'Progress',
options: options,
lineAtIndex: [2,4,8],
})
The code above inserts red vertical lines at positions 2,4 and 8, running through points of first dataset at those positions.
I'd highly recommend to use the Chartjs-Plugin-Annotation.
An example can be found at CodePen
var chartData = {
labels: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
datasets: [
{
data: [12, 3, 2, 1, 8, 8, 2, 2, 3, 5, 7, 1]
}
]
};
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
new Chart(ctx, {
type: "line",
data: chartData,
options: {
annotation: {
annotations: [
{
type: "line",
mode: "vertical",
scaleID: "x-axis-0",
value: "MAR",
borderColor: "red",
label: {
content: "TODAY",
enabled: true,
position: "top"
}
}
]
}
}
});
};
Have a look here for more Details: https://stackoverflow.com/a/36431041
I had to go through the trouble of figuring out how to do something similar with ChartJS 2.0 so I thought I would share.
This is based on the new way of overriding a chart prototype as explained here: https://github.com/chartjs/Chart.js/issues/2321
var ctx = document.getElementById('income-chart');
var originalDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function (ease) {
originalDraw.call(this, ease);
var point = dataValues[vm.incomeCentile];
var scale = this.chart.scales['x-axis-0'];
// calculate the portion of the axis and multiply by total axis width
var left = (point.x / scale.end * (scale.right - scale.left));
// draw line
this.chart.chart.ctx.beginPath();
this.chart.chart.ctx.strokeStyle = '#ff0000';
this.chart.chart.ctx.moveTo(scale.left + left, 0);
this.chart.chart.ctx.lineTo(scale.left + left, 1000000);
this.chart.chart.ctx.stroke();
// write label
this.chart.chart.ctx.textAlign = 'center';
this.chart.chart.ctx.fillText('YOU', scale.left + left, 200);
};
With chart.js 3.8.0 I've used a combo between line/bar chart with timeline (xAxis) and percentage (yAxis). See docs
The dataset configuration provides a option to set the maxBarThickness
(I've applied 2) and then apply the max value of the y-axis on each data entry of the bar chart.
Example of dataset configuration:
datasets: [
{
type: 'line'
data: [
{x: "2022-07-18", y: 10},
{x: "2022-07-19", y: 60},
{x: "2022-07-20", y: 30}
],
....
},
{
type: 'bar',
data: [
{x: "2022-07-19", y: 100}
],
maxBarThickness: 2,
...
}
]
Example of the output:
Here's a pen that achieves a similar effect without the chartjs-plugin-annotation, or hacking how Chart.js renders, or any other plugins: https://codepen.io/gkemmey/pen/qBWZbYM
min: 0
and max: 1
. Anytime you want to draw a vertical line, add a data object like { x: where_the_line_goes, y: 1 }
to your bar chart dataset.Typescript version of the vertical line plugin by Tomáš Dvořák for chart.js 2.9.4 and above:
import Chart from 'chart.js'
import {ChartOptions} from 'chart.js'
export interface ChartVerticalLine {
pointIndex: number
label: string
color: string
nPoints: number
}
export const verticalLinePlugin = {
id: 'verticalLines',
getLinePosition: function(chart: Chart, pointIndex: number) {
const meta = chart.getDatasetMeta(0) // first dataset is used to discover X coordinate of a point
const data = meta.data
const returnVal = data[pointIndex]._model.x
return returnVal
},
renderVerticalLine: function(chartInstance: Chart, pointIndex: number, text: string, color: string, nPoints: number) {
const lineLeftOffset = this.getLinePosition(chartInstance, pointIndex)
const area = chartInstance.chartArea
const context = chartInstance.ctx
// render vertical line
if (context !== null) {
context.beginPath()
context.strokeStyle = color || '#ff0000'
context.moveTo(lineLeftOffset, area ? .top)
context.lineTo(lineLeftOffset, area ? .bottom)
context.stroke()
// write label
context.fillStyle = color || '#ff0000'
const leftQuartile = 0.25 * nPoints
const rightQuartile = 0.75 * nPoints
context.textAlign = pointIndex < leftQuartile ? 'left' : pointIndex > rightQuartile ? 'right' : 'center'
const offsetText = pointIndex < leftQuartile ? ` ${text}` : pointIndex > rightQuartile ? `${text} ` : ` ${text} `
context.fillText(offsetText || '', lineLeftOffset, area.top + 6)
}
},
afterDatasetsDraw: function(chart: Chart, args: any, options: any) {
if (options.lines) {
options.lines.forEach((line: ChartVerticalLine) =>
this.renderVerticalLine(chart, line.pointIndex, line.label, line.color, line.nPoints)
)
}
},
}
Then register the plugin:
Chart.plugins.register(verticalLinePlugin)
To use the plugin, just include it as one of the plugin attributes set in the chart options:
const options: ChartOptions = {
...otherChartOptions,
plugins: {
verticalLines: {
lines: [
{
pointIndex: 100,
label: 'First Line',
color: '#ff0000',
nPoints: 1000
},
{
pointIndex: 200,
label: 'Second Line',
color: '#ff0000',
nPoints: 1000
},
{
pointIndex: 300,
label: 'Third Line',
color: '#ff0000',
nPoints: 1000
},
],
},
}
}
Enhanced version of @Tomáš Dvořák answer
Supports:
const verticalLinePlugin = {
getLinePosition: function (chart, pointIndex) {
const meta = chart.getDatasetMeta(0); // first dataset is used to discover X coordinate of a point
const data = meta.data;
return data[pointIndex]._model.x;
},
renderVerticalLine: function (chartInstance, pointIndex, label, color, alignment, xOffset, yOffset) {
const lineLeftOffset = this.getLinePosition(chartInstance, pointIndex);
const scale = chartInstance.scales['y-axis-0'];
const context = chartInstance.chart.ctx;
if (xOffset == undefined) xOffset = 0;
if (yOffset == undefined) yOffset = 0;
// render vertical line
context.beginPath();
context.strokeStyle = color;
context.moveTo(lineLeftOffset, scale.top);
context.lineTo(lineLeftOffset, scale.bottom);
context.stroke();
// write label
context.fillStyle = color;
context.textAlign = alignment;
context.fillText(label, lineLeftOffset + xOffset, (scale.bottom - scale.top) / 2 + scale.top + yOffset);
},
afterDatasetsDraw: function (chart, easing) {
if (chart.config.lineAtIndex) {
labelIndex = 0;
chart.config.lineAtIndex.forEach((pointIndex) => {
if (chart.config.verticalLinesLabels != undefined) { // if array of labels exists...
label = chart.config.verticalLinesLabels[labelIndex]; // chart.config.verticalLinesLabels must contain all elements; use elements ="" for lines not requiring labels
color = chart.config.verticalLinesColors[labelIndex]; // chart.config.verticalLinesColors must contain all elements
alignment = chart.config.verticalLinesAlignments[labelIndex]; // chart.config.verticalLinesAlignments must contain all elements
xOff = chart.config.verticalLinesX[labelIndex]; // chart.config.verticalLinesX must contain all elements
yOff = chart.config.verticalLinesY[labelIndex]; // chart.config.verticalLinesY must contain all elements
} else {
label = "";
}
this.renderVerticalLine(chart, pointIndex, label, color, alignment, xOff, yOff)
labelIndex++;
});
}
}
};
Chart.plugins.register(verticalLinePlugin);
Usage:
myChart.config.verticalLinesLabels = ["aaa", "bbb", "ddd"];
myChart.config.verticalLinesColors = ["#FF0000", 'rgb(0,255,0)', 'rgba(0,0,255,0.5)'];
myChart.config.verticalLinesAlignments = ["left", "center", "right"]; // Set label aligment (note: it is inverted because referred to line, not to label)
myChart.config.verticalLinesX = [10,5,0]; // Set label X offset
myChart.config.verticalLinesY = [10,5,0]; // Set label Y offset
myChart.config.lineAtIndex = [10,30,50]; // Mandatory to enable all previous ones
myChart.update()