在Chart.js中为Linechart添加第二个Y轴?

时间:2015-06-24 10:22:22

标签: linechart chart.js

我想在线图上添加第二个Y-axis,可能在canvas的右侧。我尝试使用Chart.js中的LineDoubleY Chart.js来定义Y-axis。的

  1. 我在Firefox上看不到这个例子:
  2.   

    ReferenceError:xPos未定义Chart.js:1147:7

    1. 如果我在我的应用中使用此{{1}}:
    2.   

      currentChart.addData不是函数

      这说:是否可以用另一种方式添加第二个{{1}}?

2 个答案:

答案 0 :(得分:9)

以下是original的修订版,具有更大的灵活性。逻辑几乎相同,但扩展到超过2个数据集

预览

enter image description here

<强>脚本

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
        }
    }
});

使用附加属性(y2axis:true)将数据集发送到y2轴。例如

{
    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
}

小提琴 - 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

的地方

答案 1 :(得分:1)

https://github.com/Wikunia/Chart.js/tree/Double-Y-Axis是从Chart.js的早期(2年后)版本中分出来的。

  

currentChart.addData不是函数

fork没有此功能。这就是您收到此错误的原因。

顺便说一下,您可能想看一下Chart.js的最新版本。 Alpha版本中存在相关问题 - https://github.com/nnnick/Chart.js/issues/17

  

这说:是否可以用另一种方式添加第二个Y轴?

目前的版本?是的,如果你愿意在几个方案上妥协。我相信你也可以解决这些妥协,但增加的复杂性有点太多了: - )

高级别步骤

  1. 其中一个数据集驱动比例 - 选择其他数据集,根据它单独计算比例,然后根据此比例和实际比例标准化值
  2. 您不希望工具提示显示标准化值,因此您需要修改工具提示功能以使值非常规化
  3. 渲染辅助y轴的标签
  4. 前两个可以在初始化覆盖中完成,最后一个在绘制覆盖中完成。

    声明和初始化

    当然,我们需要先扩展图表。让我们从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;
    }
    

    渲染辅助轴标签

    首先确保右侧有足够的空间

    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
    }
    

    我们完成了!

    小提琴 - http://jsfiddle.net/u2Lru6vv/

    注意 - 使用镜像y轴覆盖2个图表(就像我们上面所做的那样)是另一个(略微侵入性较小)选项,但问题是您丢失了基础图表的工具提示。