在 Chart.js 中为折线图添加第二个 Y 轴?
Add a second Y-axis for Linechart in Chart.js?
我想在折线图上添加第二个 Y-axis
,可能在 canvas
的右侧。我尝试使用取自 https://github.com/Wikunia/Chart.js/tree/Double-Y-Axis 的 Chart.js
,其中定义了 LineDoubleY
。 但是:
- 我在 Firefox 上看不到示例:
ReferenceError: xPos is not defined Chart.js:1147:7
- 如果我在我的应用程序中使用此
Chart.js
:
currentChart.addData is not a function
这说:是否可以用另一种方式添加第二个Y-axis
?
https://github.com/Wikunia/Chart.js/tree/Double-Y-Axis 是从 Chart.js
的早期(2 年前)版本分叉出来的
currentChart.addData is not a function
叉子没有这个功能。这就是您收到此错误的原因。
顺便看看最新版的Chart.js。 alpha 版本中存在一个相关问题 - https://github.com/nnnick/Chart.js/issues/17
This said: Is it possible to add a second Y-axis in another way?
用当前版本?是的,如果您愿意在几个选项上妥协。我相信您也可以解决这些妥协,但增加的复杂性有点太多了:-)
高级步骤
- 其中一个数据集驱动比例 - 选择 其他 数据集,仅基于它计算出比例,然后根据该比例和实际比例对值进行归一化
- 您不希望工具提示显示规范化值,因此您需要修改工具提示函数以对值进行反规范化
- 渲染次要 y 轴的标签
前 2 个可以在初始化覆盖中完成,最后 1 个可以在绘制覆盖中完成。
声明和初始化
当然,我们需要先扩展图表。让我们从 0 开始缩放并关闭网格线以降低复杂性。
所以
Chart.types.Line.extend({
name: "Line2Y",
和
var ctx = document.getElementById("chart").getContext("2d");
var myLine1 = new Chart(ctx).Line2Y(lineChartData1, {
scaleBeginAtZero: true,
scaleShowGridLines: false
});
计算归一化因子
initialize: function (data) {
// figure out which dataset has the max value - that is the one that drives the scale
var max = 0;
var datasetToNotScale = 0;
var datasetToScale = 1;
data.datasets.forEach(function (dataset, i) {
dataset.data.forEach(function (e) {
if (e > max) {
max = e;
datasetToNotScale = i;
datasetToScale = (i == 0 ? 1 : 0);
}
})
})
var datasetToScaleLabel = data.datasets[datasetToScale].label;
var startPoint = this.options.scaleFontSize;
var endPoint = this.chart.height - (this.options.scaleFontSize * 1.5) - 5;
// use the helper function to get the scale for both datasets
var notScaleRange = Chart.helpers.calculateScaleRange(
data.datasets[datasetToNotScale].data,
endPoint - startPoint,
this.options.scaleFontSize,
this.options.scaleBeginAtZero,
this.options.scaleIntegersOnly
)
this.scaleRange = Chart.helpers.calculateScaleRange(
data.datasets[datasetToScale].data,
endPoint - startPoint,
this.options.scaleFontSize,
this.options.scaleBeginAtZero,
this.options.scaleIntegersOnly
)
获得两个数据集的比例后,计算归一化因子(两个比例最大值的比率,因为我们将图表比例设置为从 0 开始)
var normalizingFactor = notScaleRange.max / this.scaleRange.max;
规范化(用于绘图)和反规范化(用于工具提示)
使用它来更新不驱动规模
的数据集
// update one of our datasets!
data.datasets[datasetToScale].data.forEach(function (e, i) {
data.datasets[datasetToScale].data[i] = e * normalizingFactor;
})
当然,通过在工具提示函数中进行反规范化来抵消这种情况(注意 Math.round - 它会处理来回转换时精度的轻微损失)
this.options.multiTooltipTemplate = function (d) {
if (d.datasetLabel == datasetToScaleLabel)
return Math.round(d.value / normalizingFactor, 6);
else
return d.value;
}
渲染辅助轴标签
首先确保你有足够的 space 在右边
draw: function () {
this.scale.xScalePaddingRight = this.scale.xScalePaddingLeft;
然后,绘制实际图表后,绘制我们的辅助轴标签
this.chart.ctx.font = Chart.helpers.fontString(self.fontSize, self.fontStyle, self.fontFamily)
this.chart.ctx.textAlign = 'left';
this.chart.ctx.textBaseline = "middle";
this.chart.ctx.fillStyle = "#666";
var label = this.scaleRange.min;
var yStep = (this.scale.endPoint - this.scale.startPoint) / this.scaleRange.steps
for (var i = 0, y = this.scale.endPoint; i <= this.scaleRange.steps; i++) {
this.chart.ctx.fillText(label, this.chart.width - this.scale.xScalePaddingRight + 10, y);
y -= yStep;
label += this.scaleRange.stepValue
}
我们完成了!
Fiddle - http://jsfiddle.net/u2Lru6vv/
注意 - 用镜像 y 轴叠加 2 个图表(就像我们上面所做的那样)是另一种(侵入性稍小)选项,但问题是您丢失了底层图表的工具提示。
这是 的修订版,更加灵活。逻辑几乎相同,但扩展到 2 个以上的数据集
预览
脚本
Chart.types.Line.extend({
name: "Line2Y",
getScale: function(data) {
var startPoint = this.options.scaleFontSize;
var endPoint = this.chart.height - (this.options.scaleFontSize * 1.5) - 5;
return Chart.helpers.calculateScaleRange(
data,
endPoint - startPoint,
this.options.scaleFontSize,
this.options.scaleBeginAtZero,
this.options.scaleIntegersOnly);
},
initialize: function (data) {
var y2datasetLabels = [];
var y2data = [];
var y1data = [];
data.datasets.forEach(function (dataset, i) {
if (dataset.y2axis == true) {
y2datasetLabels.push(dataset.label);
y2data = y2data.concat(dataset.data);
} else {
y1data = y1data.concat(dataset.data);
}
});
// use the helper function to get the scale for both datasets
var y1Scale = this.getScale(y1data);
this.y2Scale = this.getScale(y2data);
var normalizingFactor = y1Scale.max / this.y2Scale.max;
// update y2 datasets
data.datasets.forEach(function(dataset) {
if (y2datasetLabels.indexOf(dataset.label) !== -1) {
dataset.data.forEach(function (e, j) {
dataset.data[j] = e * normalizingFactor;
})
}
})
// denormalize tooltip for y2 datasets
this.options.multiTooltipTemplate = function (d) {
if (y2datasetLabels.indexOf(d.datasetLabel) !== -1)
return Math.round(d.value / normalizingFactor, 6);
else
return d.value;
}
Chart.types.Line.prototype.initialize.apply(this, arguments);
},
draw: function () {
this.scale.xScalePaddingRight = this.scale.xScalePaddingLeft;
Chart.types.Line.prototype.draw.apply(this, arguments);
this.chart.ctx.textAlign = 'left';
this.chart.ctx.textBaseline = "middle";
this.chart.ctx.fillStyle = "#666";
var yStep = (this.scale.endPoint - this.scale.startPoint) / this.y2Scale.steps
for (var i = 0, y = this.scale.endPoint, label = this.y2Scale.min;
i <= this.y2Scale.steps;
i++) {
this.chart.ctx.fillText(label, this.chart.width - this.scale.xScalePaddingRight + 10, y);
y -= yStep;
label += this.y2Scale.stepValue
}
}
});
您将数据集发送到 y2 轴并附加 属性(y2 轴:true)。例如
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
data: [150, 48, 120, 19, 46, 27, 100],
y2axis: true
}
Fiddle - http://jsfiddle.net/1va2kx18/
您可以为 y 轴上的系列使用一种颜色阴影,为 y2 轴上的颜色使用另一种颜色(否则会有点混乱)。此外,您可以修改工具提示函数以稍微不同地显示 y2 值。例如
return '[' + Math.round(d.value / normalizingFactor, 6) + ']';
将在工具提示中将 y2 值放在方括号中
如果您使用 addData 向数据集添加新点,则 issue 在新添加的点中数据集标签未更新,您必须通过更新 addData 函数来解决这个问题。
如果您不想这样做,如果您对 y 和 y2 系列使用不同的点颜色,则只需使用数据集点颜色(而不是使用数据集标签)来区分 y 和 y2 系列。以下是要替换的行
var y2datasetColors = [];
...
y2datasetColors.push(dataset.pointColor);
...
if (y2datasetColors.indexOf(dataset.pointColor) !== -1) {
...
if (y2datasetColors.indexOf(d._saved.fillColor) !== -1)
您之前 y2datasets
我想在折线图上添加第二个 Y-axis
,可能在 canvas
的右侧。我尝试使用取自 https://github.com/Wikunia/Chart.js/tree/Double-Y-Axis 的 Chart.js
,其中定义了 LineDoubleY
。 但是:
- 我在 Firefox 上看不到示例:
ReferenceError: xPos is not defined Chart.js:1147:7
- 如果我在我的应用程序中使用此
Chart.js
:
currentChart.addData is not a function
这说:是否可以用另一种方式添加第二个Y-axis
?
https://github.com/Wikunia/Chart.js/tree/Double-Y-Axis 是从 Chart.js
的早期(2 年前)版本分叉出来的currentChart.addData is not a function
叉子没有这个功能。这就是您收到此错误的原因。
顺便看看最新版的Chart.js。 alpha 版本中存在一个相关问题 - https://github.com/nnnick/Chart.js/issues/17
This said: Is it possible to add a second Y-axis in another way?
用当前版本?是的,如果您愿意在几个选项上妥协。我相信您也可以解决这些妥协,但增加的复杂性有点太多了:-)
高级步骤
- 其中一个数据集驱动比例 - 选择 其他 数据集,仅基于它计算出比例,然后根据该比例和实际比例对值进行归一化
- 您不希望工具提示显示规范化值,因此您需要修改工具提示函数以对值进行反规范化
- 渲染次要 y 轴的标签
前 2 个可以在初始化覆盖中完成,最后 1 个可以在绘制覆盖中完成。
声明和初始化
当然,我们需要先扩展图表。让我们从 0 开始缩放并关闭网格线以降低复杂性。
所以
Chart.types.Line.extend({
name: "Line2Y",
和
var ctx = document.getElementById("chart").getContext("2d");
var myLine1 = new Chart(ctx).Line2Y(lineChartData1, {
scaleBeginAtZero: true,
scaleShowGridLines: false
});
计算归一化因子
initialize: function (data) {
// figure out which dataset has the max value - that is the one that drives the scale
var max = 0;
var datasetToNotScale = 0;
var datasetToScale = 1;
data.datasets.forEach(function (dataset, i) {
dataset.data.forEach(function (e) {
if (e > max) {
max = e;
datasetToNotScale = i;
datasetToScale = (i == 0 ? 1 : 0);
}
})
})
var datasetToScaleLabel = data.datasets[datasetToScale].label;
var startPoint = this.options.scaleFontSize;
var endPoint = this.chart.height - (this.options.scaleFontSize * 1.5) - 5;
// use the helper function to get the scale for both datasets
var notScaleRange = Chart.helpers.calculateScaleRange(
data.datasets[datasetToNotScale].data,
endPoint - startPoint,
this.options.scaleFontSize,
this.options.scaleBeginAtZero,
this.options.scaleIntegersOnly
)
this.scaleRange = Chart.helpers.calculateScaleRange(
data.datasets[datasetToScale].data,
endPoint - startPoint,
this.options.scaleFontSize,
this.options.scaleBeginAtZero,
this.options.scaleIntegersOnly
)
获得两个数据集的比例后,计算归一化因子(两个比例最大值的比率,因为我们将图表比例设置为从 0 开始)
var normalizingFactor = notScaleRange.max / this.scaleRange.max;
规范化(用于绘图)和反规范化(用于工具提示)
使用它来更新不驱动规模
的数据集// update one of our datasets!
data.datasets[datasetToScale].data.forEach(function (e, i) {
data.datasets[datasetToScale].data[i] = e * normalizingFactor;
})
当然,通过在工具提示函数中进行反规范化来抵消这种情况(注意 Math.round - 它会处理来回转换时精度的轻微损失)
this.options.multiTooltipTemplate = function (d) {
if (d.datasetLabel == datasetToScaleLabel)
return Math.round(d.value / normalizingFactor, 6);
else
return d.value;
}
渲染辅助轴标签
首先确保你有足够的 space 在右边
draw: function () {
this.scale.xScalePaddingRight = this.scale.xScalePaddingLeft;
然后,绘制实际图表后,绘制我们的辅助轴标签
this.chart.ctx.font = Chart.helpers.fontString(self.fontSize, self.fontStyle, self.fontFamily)
this.chart.ctx.textAlign = 'left';
this.chart.ctx.textBaseline = "middle";
this.chart.ctx.fillStyle = "#666";
var label = this.scaleRange.min;
var yStep = (this.scale.endPoint - this.scale.startPoint) / this.scaleRange.steps
for (var i = 0, y = this.scale.endPoint; i <= this.scaleRange.steps; i++) {
this.chart.ctx.fillText(label, this.chart.width - this.scale.xScalePaddingRight + 10, y);
y -= yStep;
label += this.scaleRange.stepValue
}
我们完成了!
Fiddle - http://jsfiddle.net/u2Lru6vv/
注意 - 用镜像 y 轴叠加 2 个图表(就像我们上面所做的那样)是另一种(侵入性稍小)选项,但问题是您丢失了底层图表的工具提示。
这是
预览
脚本
Chart.types.Line.extend({
name: "Line2Y",
getScale: function(data) {
var startPoint = this.options.scaleFontSize;
var endPoint = this.chart.height - (this.options.scaleFontSize * 1.5) - 5;
return Chart.helpers.calculateScaleRange(
data,
endPoint - startPoint,
this.options.scaleFontSize,
this.options.scaleBeginAtZero,
this.options.scaleIntegersOnly);
},
initialize: function (data) {
var y2datasetLabels = [];
var y2data = [];
var y1data = [];
data.datasets.forEach(function (dataset, i) {
if (dataset.y2axis == true) {
y2datasetLabels.push(dataset.label);
y2data = y2data.concat(dataset.data);
} else {
y1data = y1data.concat(dataset.data);
}
});
// use the helper function to get the scale for both datasets
var y1Scale = this.getScale(y1data);
this.y2Scale = this.getScale(y2data);
var normalizingFactor = y1Scale.max / this.y2Scale.max;
// update y2 datasets
data.datasets.forEach(function(dataset) {
if (y2datasetLabels.indexOf(dataset.label) !== -1) {
dataset.data.forEach(function (e, j) {
dataset.data[j] = e * normalizingFactor;
})
}
})
// denormalize tooltip for y2 datasets
this.options.multiTooltipTemplate = function (d) {
if (y2datasetLabels.indexOf(d.datasetLabel) !== -1)
return Math.round(d.value / normalizingFactor, 6);
else
return d.value;
}
Chart.types.Line.prototype.initialize.apply(this, arguments);
},
draw: function () {
this.scale.xScalePaddingRight = this.scale.xScalePaddingLeft;
Chart.types.Line.prototype.draw.apply(this, arguments);
this.chart.ctx.textAlign = 'left';
this.chart.ctx.textBaseline = "middle";
this.chart.ctx.fillStyle = "#666";
var yStep = (this.scale.endPoint - this.scale.startPoint) / this.y2Scale.steps
for (var i = 0, y = this.scale.endPoint, label = this.y2Scale.min;
i <= this.y2Scale.steps;
i++) {
this.chart.ctx.fillText(label, this.chart.width - this.scale.xScalePaddingRight + 10, y);
y -= yStep;
label += this.y2Scale.stepValue
}
}
});
您将数据集发送到 y2 轴并附加 属性(y2 轴:true)。例如
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
data: [150, 48, 120, 19, 46, 27, 100],
y2axis: true
}
Fiddle - http://jsfiddle.net/1va2kx18/
您可以为 y 轴上的系列使用一种颜色阴影,为 y2 轴上的颜色使用另一种颜色(否则会有点混乱)。此外,您可以修改工具提示函数以稍微不同地显示 y2 值。例如
return '[' + Math.round(d.value / normalizingFactor, 6) + ']';
将在工具提示中将 y2 值放在方括号中
如果您使用 addData 向数据集添加新点,则 issue 在新添加的点中数据集标签未更新,您必须通过更新 addData 函数来解决这个问题。
如果您不想这样做,如果您对 y 和 y2 系列使用不同的点颜色,则只需使用数据集点颜色(而不是使用数据集标签)来区分 y 和 y2 系列。以下是要替换的行
var y2datasetColors = [];
...
y2datasetColors.push(dataset.pointColor);
...
if (y2datasetColors.indexOf(dataset.pointColor) !== -1) {
...
if (y2datasetColors.indexOf(d._saved.fillColor) !== -1)
您之前 y2datasets