更新 - 此答案适用于 Chart.js 1.x,如果您正在寻找 2.x 答案,请检查评论和其他答案。
您可以扩展折线图并在绘图函数中包含用于绘制线条的逻辑。
预览
HTML
<div>
<canvas id="LineWithLine" width="600" height="400"></canvas>
</div>
脚本
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
});
选项属性 lineAtIndex 控制在哪个点绘制线。
分享我的 Chartjs.org 版本 2.5 的解决方案。我想使用一个插件,使实现可重用。
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);
使用方法很简单:
new Chart(ctx, {
type: 'line',
data: data,
label: 'Progress',
options: options,
lineAtIndex: [2,4,8],
})
上面的代码在位置 2,4 和 8 处插入红色垂直线,穿过这些位置处的第一个数据集的点。
我强烈建议使用Chartjs-Plugin-Annotation。
示例可以在 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"
}
}
]
}
}
});
};
查看此处了解更多详细信息:https://stackoverflow.com/a/36431041
使用 Chart.js 3.8.0 我使用了带有时间线(xAxis)和百分比(yAxis)的折线图/条形图的组合。请参阅文档
数据集配置提供了一个选项来设置
maxBarThickness
(我已经应用了 2 个),然后将 y 轴的最大值应用于条形图的每个数据条目。
数据集配置示例:
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,
...
}
]
输出示例:
我不得不费尽心思弄清楚如何使用 ChartJS 2.0 做类似的事情,所以我想我会分享。
这是基于重写图表原型的新方法,如下所述: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);
};
这里有一支笔可以实现类似的效果,无需使用 Chartjs-plugin-annotation,或者修改 Chart.js 的渲染方式,或任何其他插件:https://codepen.io/gkemmey/pen/qBWZbYM
min: 0
和 max: 1
。每当您想要绘制垂直线时,请将 { x: where_the_line_goes, y: 1 }
之类的数据对象添加到条形图数据集中。@Tomáš Dvořák 答案的增强版
支持:
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);
用途:
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()
Tomáš Dvořák 的垂直线插件 Typescript 版本,适用于 Chart.js 2.9.4 及更高版本:
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)
)
}
},
}
然后注册插件:
Chart.plugins.register(verticalLinePlugin)
要使用该插件,只需将其作为图表选项中设置的插件属性之一即可:
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
},
],
},
}
}
(叉@potatopeelings答案:
https://stackoverflow.com/a/30323431/3114914)
演示: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);
for(let item in this.options.lineAtIndex){
var point = this.datasets[0].points[this.options.lineAtIndex[item]['lableIndex']]
var scale = this.scale
// draw line
this.chart.ctx.beginPath();
this.chart.ctx.moveTo(point.x, scale.startPoint + 24);
this.chart.ctx.strokeStyle = this.options.lineAtIndex[item]['color'];
this.chart.ctx.lineTo(point.x, scale.endPoint);
this.chart.ctx.stroke();
// write TEXT
this.chart.ctx.textAlign = 'center';
this.chart.ctx.fillText(item, point.x, scale.startPoint + 12);
}
}
});
new Chart(ctx).LineWithLine(data, {
datasetFill : false,
lineAtIndex: {
"Text1":{
"lableIndex":2,
"color":'#ff0000'
},
"Text2":{
"lableIndex":5,
"color":'#3ca832'
},
"Text3":{
"lableIndex":6,
"color":'#2200ff'
}
}
});