动态更新chartjs图的比例类型

时间:2017-03-06 12:23:16

标签: javascript charts chart.js

我在页面上有一个条形图,其中显示的数据是动态更新的。我遇到的问题是我需要根据显示的数据更改y轴的类型。当数据的范围小于100(例如0-99)时,我需要它是线性标度,如果数据的范围小于100(例如0-1000),我需要它是对数标准。

我可以计算出我是否需要对数刻度或不精确,但是当涉及到更改刻度类型时我感到茫然。我尝试更改图表的options对象并调用update()

myChart.options/scales.yAxes.map(function (axis) { axis.type = 'logarithmic' });
myChart.update();

并尝试直接更改比例本身:

Object.keys(mychart.scales).forEach((function (axisName) {
            var axis = myChart.scales[axisName];
            if (axis && axis.id.startsWith('y')) {
                axis.options.type = 'logarithmic'
            }
        }));
myChart.update();

然后我尝试使用beforeBuildTicks回调,认为在更新数据和重绘图表时更改类型可能太晚或只是在错误的时间:

beforeBuildTicks: function (scale) {
                                if (scale.chart) {
                                    console.log('logarithmic');
                                    scale.options.type = 'logarithmic';
                                 }
                            }

所有这些方法都会更改图表对象,但显示不会受到影响。

有没有办法在不破坏和重新创建整个图表的情况下动态更改chartjs条形图的比例类型?

3 个答案:

答案 0 :(得分:1)

我终于找到了解决方案,但这并不简单。

感谢@jordanwillis在使用`scaleMerge'方面指向正确的方向。辅助功能。

我创建了一个完成所有工作的plugin

var changeScaleTypePlugin = {
beforeUpdate: function (chartInstance) {
    var self = this;
    chartInstance.beforeInit = null;
    if (chartInstance.options.changeScale) {
        self.changeScales(chartInstance);
        if (chartInstance.options.scaleTypeChanged) {
            chartInstance.options.scaleTypeChanged = false;
            Object.keys(chartInstance.scales).forEach(function (axisName) {
                var scale = chartInstance.scales[axisName];
                Chart.layoutService.removeBox(chartInstance, scale);
            });
            chartInstance.initialize();
        }
    }
},
changeScales: function (chartInstance) {
    var maxValue = Math.max.apply(null, chartInstance.data.datasets.map(function (dataset) { return Math.max.apply(null, dataset.data); }));
    var minValue = Math.min.apply(null, chartInstance.data.datasets.map(function (dataset) { return Math.min.apply(null, dataset.data); }));
    var logMax = Math.floor(Math.log(maxValue) / Math.LN10);
    var logMin = Math.floor(Math.log(minValue) / Math.LN10);
    if (logMax - logMin > chartInstance.options.maxRankDifference) {
        if (!chartInstance.options.scaleTypeChanged && chartInstance.options.scales.yAxes.filter(function (axis) { return axis.type !== 'logarithmic'; }).length) {
            console.log('logarithmic');
            chartInstance.options.scaleTypeChanged = true;
            chartInstance.options.scales.yAxes = Chart.helpers.scaleMerge(Chart.defaults.scale, { yAxes: chartInstance.options.logarithmicScaleOptions }).yAxes;                
        }
    } else {
        if (!chartInstance.options.scaleTypeChanged && chartInstance.options.scales.yAxes.filter(function (axis) { return axis.type !== 'linear'; }).length) {
            console.log('linear');
            chartInstance.options.scaleTypeChanged = true;
            chartInstance.options.scales.yAxes = Chart.helpers.scaleMerge(Chart.defaults.scale, { yAxes: chartInstance.options.linearScaleOptions }).yAxes;
        }
    }
}

};

Chart.pluginService.register(changeScaleTypePlugin);

要应用此功能,只需在图表选项中添加一些属性:

options: {
                     linearScaleOptions: [{
                        id: 'y-axis-0',
                         type: 'linear',
                         tick: {
                             // callback: Chart.Ticks.formatters.linear,
                             min: 0
                         }   
                     }],
                     logarithmicScaleOptions: [{
                        id: 'y-axis-0',
                        type: 'logarithmic',
                        ticks: {
                            // callback: function (tickValue, index, ticks)  {
                            //        var remain = tickValue / (Math.pow(10, Math.floor(Math.log(tickValue) / Math.LN10)));

                            //        if (tickValue === 0) {
                            //            return '0';
                            //        } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
                            //            return tickValue;
                            //        }
                            //        return '';
                            //},
                            min: 0
                        }
                    }],
                     changeScale: true,
                    maxRankDifference: 1,

在滴答声中注释掉的回调是针对这样的情况:(像我一样)你不希望在对数刻度上用科学记数法显示刻度值。

这给出了如下输出:

logarithmic

将max rank属性设置为1

linear

将其设置为3

或使用回调集:

logarithmic with natural ticks

答案 1 :(得分:0)

您处于正确的轨道上,但问题出在每个比例类型上,基础比例服务使用一组相应的属性来呈现比例。

因此,当您将比例配置为线性'时,会触发一组隐藏轴属性,以设置生成渲染线性比例。但是,当您将比例配置为“对数”时,需要以不同方式设置隐藏轴属性以生成呈现的对数比例。

长话短说,你必须使用Chart.helpers.scaleMerge()功能为你处理所有这些(你不能简单地改变图表选项中的比例类型并重新渲染)。

我创建了一个working example,展示了如何实现您的目标。其工作原理如下所示。

document.getElementById('changeScaleAndData').addEventListener('click', function() {
  if (isLinear) {
    myBar.options.scales.yAxes = Chart.helpers.scaleMerge(Chart.defaults.scale, {yAxes: logarithmicScaleOptions}).yAxes;
    isLinear = false;
  } else {
    myBar.options.scales.yAxes = Chart.helpers.scaleMerge(Chart.defaults.scale, {yAxes: linearScaleOptions}).yAxes;
    isLinear = true;
  }

  myBar.data.datasets[0].data = [
    randomScalingFactor(), 
    randomScalingFactor(), 
    randomScalingFactor(), 
  ];

  myBar.update();
});

答案 2 :(得分:0)

我发现在更改轴类型后更容易删除和重新创建图表。 IE,如果我有一个ID为logScale的复选框来设置我的轴​​是否为log,我可以说:

$("#logScale").change(function () {
    $('#chart').remove();
    createChart();
})

其中createChart是用于创建图表的代码。请注意,我引用#logScale设置Y轴类型:

$('#chartContainer').append('<canvas id="chart" style="max-height:300px;max-width:95%"></canvas>');
    var axisType = $("#logScale").prop("checked") ? 'logarithmic' : 'linear';
        ctx = document.getElementById('chart').getContext('2d');
        chartObject = new Chart(ctx, {
            options: {
                scales: {
                    xAxes: [{
                    yAxes: [{
                        type: axisType
                    }]
                } //and whatever other chart data you have