【问题标题】:Chart.js: evenly distribute ticks when using maxTicksLimitChart.js:使用 maxTicksLimit 时均匀分布刻度
【发布时间】:2021-05-24 03:32:19
【问题描述】:

我使用 Chart.js 版本 2.1.3 制作了折线图。

        var canvas = $('#gold_chart').get(0);
        var ctx = canvas.getContext('2d');
        var fillPatternGold = ctx.createLinearGradient(0, 0, 0, canvas.height);
        fillPatternGold.addColorStop(0, '#fdca55');
        fillPatternGold.addColorStop(1, '#ffffff');

        var goldChart = new Chart(ctx, {
            type: 'line',
            animation: false,
            data: {
                labels: dates,
                datasets: [{
                    label: '',
                    data: prices,
                    pointRadius: 0,
                    borderWidth: 1,
                    borderColor: '#a97f35',
                    backgroundColor: fillPatternGold
                }]
            },
            title: {
                position: 'bottom',
                text: '\u7F8E\u5143 / \u76CE\u53F8'
            },
            options: {
                legend: {
                    display: false
                },
                tooltips: {
                    callback: function(tooltipItem) {
                        return tooltipItem.yLabel;
                    }
                },
                scales: {
                    xAxes: [{
                        ticks: {
                            maxTicksLimit: 8
                        }
                    }]
                }
            }
        });

输出如下:

如您所见,我通过maxTicksLimit 将最大刻度数限制为 8。但是,分布并不均匀。如何使刻度分布均匀?

附言数据集中始终有 289 条记录,每 5 分钟记录一次数据。 prices 变量的示例值为:

[
  {"14:10", 1280.3},
  {"14:15", 1280.25},
  {"14:20", 1282.85}
]

我尝试了maxTicksLimit的不同值,结果仍然分布不均匀。

【问题讨论】:

    标签: javascript chart.js


    【解决方案1】:

    Chart.js 使用整数 skipRatio(计算要跳过多少标签)。使用 Chart.js v2.1.x,您可以编写自己的插件来使用小数 skipRatio


    预览


    脚本

    Chart.pluginService.register({
        afterUpdate: function (chart) {
            var xScale = chart.scales['x-axis-0'];
            if (xScale.options.ticks.maxTicksLimit) {
                // store the original maxTicksLimit
                xScale.options.ticks._maxTicksLimit = xScale.options.ticks.maxTicksLimit;
                // let chart.js draw the first and last label
                xScale.options.ticks.maxTicksLimit = (xScale.ticks.length % xScale.options.ticks._maxTicksLimit === 0) ? 1 : 2;
    
                var originalXScaleDraw = xScale.draw
                xScale.draw = function () {
                    originalXScaleDraw.apply(this, arguments);
    
                    var xScale = chart.scales['x-axis-0'];
                    if (xScale.options.ticks.maxTicksLimit) {
                        var helpers = Chart.helpers;
    
                        var tickFontColor = helpers.getValueOrDefault(xScale.options.ticks.fontColor, Chart.defaults.global.defaultFontColor);
                        var tickFontSize = helpers.getValueOrDefault(xScale.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
                        var tickFontStyle = helpers.getValueOrDefault(xScale.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
                        var tickFontFamily = helpers.getValueOrDefault(xScale.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
                        var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
                        var tl = xScale.options.gridLines.tickMarkLength;
    
                        var isRotated = xScale.labelRotation !== 0;
                        var yTickStart = xScale.top;
                        var yTickEnd = xScale.top + tl;
                        var chartArea = chart.chartArea;
    
                        // use the saved ticks
                        var maxTicks = xScale.options.ticks._maxTicksLimit - 1;
                        var ticksPerVisibleTick = xScale.ticks.length / maxTicks;
    
                        // chart.js uses an integral skipRatio - this causes all the fractional ticks to be accounted for between the last 2 labels
                        // we use a fractional skipRatio
                        var ticksCovered = 0;
                        helpers.each(xScale.ticks, function (label, index) {
                            if (index < ticksCovered)
                                return;
    
                            ticksCovered += ticksPerVisibleTick;
    
                            // chart.js has already drawn these 2
                            if (index === 0 || index === (xScale.ticks.length - 1))
                                return;
    
                            // copy of chart.js code
                            var xLineValue = this.getPixelForTick(index);
                            var xLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines);
    
                            if (this.options.gridLines.display) {
                                this.ctx.lineWidth = this.options.gridLines.lineWidth;
                                this.ctx.strokeStyle = this.options.gridLines.color;
    
                                xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
    
                                // Draw the label area
                                this.ctx.beginPath();
    
                                if (this.options.gridLines.drawTicks) {
                                    this.ctx.moveTo(xLineValue, yTickStart);
                                    this.ctx.lineTo(xLineValue, yTickEnd);
                                }
    
                                // Draw the chart area
                                if (this.options.gridLines.drawOnChartArea) {
                                    this.ctx.moveTo(xLineValue, chartArea.top);
                                    this.ctx.lineTo(xLineValue, chartArea.bottom);
                                }
    
                                // Need to stroke in the loop because we are potentially changing line widths & colours
                                this.ctx.stroke();
                            }
    
                            if (this.options.ticks.display) {
                                this.ctx.save();
                                this.ctx.translate(xLabelValue + this.options.ticks.labelOffset, (isRotated) ? this.top + 12 : this.options.position === "top" ? this.bottom - tl : this.top + tl);
                                this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
                                this.ctx.font = tickLabelFont;
                                this.ctx.textAlign = (isRotated) ? "right" : "center";
                                this.ctx.textBaseline = (isRotated) ? "middle" : this.options.position === "top" ? "bottom" : "top";
                                this.ctx.fillText(label, 0, 0);
                                this.ctx.restore();
                            }
                        }, xScale);
                    }
                };
            }
        },
    });
    

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

    【讨论】:

    • 哇,这完美!我认为 Chart.js 应该集成您的代码以使 maxTicksLimit 更好地工作。
    • 经过进一步测试,当画布变小时,无法正常显示刻度随机(只显示第一个和最后一个刻度);有时它会在刷新后正确显示。看到这个:imgur.com/RtgYWZc
    • 感谢您的发现!我将 maxTicksCount 更改为 2 - 这实际上导致它在下一次将其视为 2(在工具提示更新期间强制重绘时)。此外,当绘制工具提示时,显然没有通知插件。通过覆盖scale.draw 来解决这个问题。试一试,如果我破坏了其他任何东西,请告诉我 :-) 干杯!
    • 均匀定位蜱虫似乎有点过头了?我的意思是,他们不应该升级他们的逻辑吗?
    • 感谢您的解决方案。很好。但是我用最新版本的 Chart.js (2.7.2) 测试了最后更新的小提琴 -> jsfiddle.net/xk5d48yt/1 看起来它们必须更改一些东西,所以现在它画错了。这是示例:jsfiddle.net/xk5d48yt/6
    【解决方案2】:

    在 Chart JS 贡献者永久修复此问题之前,一个更简单的解决方案是在 maxTicksLimit 中包含一个小数。

    例如:

    maxTicksLimit: 8,
    

    最后会产生巨大的差距。

    maxTicksLimit: 8.1,
    

    最后不会产生巨大的差距。

    根据您想要将 maxTicksLimit 设置为的值,您需要使用不同的小数来查看哪一个会产生最佳结果。

    【讨论】:

    • 这是值得注意的。 2 年后,我想知道他们是否解决了这个问题。
    • 我使用的是最新版本,但问题仍然存在于两个轴上 :(
    • 对我来说仍然是个问题。此解决方案大部分时间都有效,但偶尔在最后一列仍有较大的差距。我有一个图表,用户可以重新绘制以放大和缩小,并且在其中一个视图上,最后一列会太宽。
    【解决方案3】:

    这样做:

    yAxes: [{
        ticks: {
            stepSize: Math.round((Math.max.apply(Math, myListOfyValues) / 10)/5)*5,
            beginAtZero: true,
            precision: 0
        }
    }]
    

    10 = 滴答数

    5 = 将刻度值四舍五入到最接近的 5 - 所有 y 值将均匀递增

    类似的方法也适用于 xAxes。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-14
      • 2012-07-10
      • 1970-01-01
      • 2011-04-04
      • 1970-01-01
      • 2013-01-05
      相关资源
      最近更新 更多