【问题标题】:chartjs - top and bottom padding of a chart areachartjs - 图表区域的顶部和底部填充
【发布时间】:2021-10-02 06:06:31
【问题描述】:

我需要在图表区域上方和下方(接近顶部和底部刻度)添加更多空间。 似乎只有向垂直轴添加填充的能力。 我根据文档禁用了刻度线: http://www.chartjs.org/docs/#scales:

Chart.defaults.scale.gridLines.drawTicks = false;

chartjs 图表区域顶部和底部填充图像:

我还可以向垂直轴刻度标签(刻度)添加填充

Chart.defaults.scale.ticks.padding = 15;

如何在顶部刻度上方和底部(零)刻度下方添加填充?

【问题讨论】:

    标签: chart.js


    【解决方案1】:

    有几种方法可以控制 chart.js 中比例/图例之间的填充(文档中记录了一些官方方法和其他地方描述的一些“hacky”方法)。问题是,没有办法使用现有的配置选项来控制整个图表的填充(左比例、下比例、上比例......或图例的底部等)。

    幸运的是,由于 chart.js 的灵活接口(并且因为我们可以创建新的比例类型等),仍然可以控制填充而不用大惊小怪。让我解释一下添加左侧、顶部和底部填充的方法,然后在最后提供一个工作示例(如果您愿意,请跳过)。

    左填充

    这个很简单。有一个记录在案的选项可以控制这一点。只需将scales.yAxes.ticks.padding 选项设置为您想要的任何值。这是一个例子。

    scales: {
      yAxes: [{
        ticks: {
          beginAtZero: true,
          padding: 25,
        }
      }]
    }
    

    顶部填充(或图例填充)

    没有选项可以控制它,所以我们必须构建它。我通过创建一个新的 Legend 对象并覆盖使用选项对象上设置的 paddingBottom 选项的 afterFit() 函数来构建它。这不是太难,但需要绕道而行。这是相关代码。

    function getBoxWidth(labelOpts, fontSize) {
      return labelOpts.usePointStyle ?
        fontSize * Math.SQRT2 :
      labelOpts.boxWidth;
    };
    
    Chart.NewLegend = Chart.Legend.extend({
      afterFit: function() {
        this.height = this.height + this.options.paddingBottom;
      },
    });
    
    function createNewLegendAndAttach(chartInstance, legendOpts) {
      var legend = new Chart.NewLegend({
        ctx: chartInstance.chart.ctx,
        options: legendOpts,
        chart: chartInstance
      });
    
      if (chartInstance.legend) {
        Chart.layoutService.removeBox(chartInstance, chartInstance.legend);
        delete chartInstance.newLegend;
      }
    
      chartInstance.newLegend = legend;
      Chart.layoutService.addBox(chartInstance, legend);
    }
    
    // Register the legend plugin
    Chart.plugins.register({
      beforeInit: function(chartInstance) {
        var legendOpts = chartInstance.options.legend;
    
        if (legendOpts) {
          createNewLegendAndAttach(chartInstance, legendOpts);
        }
      },
      beforeUpdate: function(chartInstance) {
        var legendOpts = chartInstance.options.legend;
    
        if (legendOpts) {
          legendOpts = Chart.helpers.configMerge(Chart.defaults.global.legend, legendOpts);
    
          if (chartInstance.newLegend) {
            chartInstance.newLegend.options = legendOpts;
          } else {
            createNewLegendAndAttach(chartInstance, legendOpts);
          }
        } else {
          Chart.layoutService.removeBox(chartInstance, chartInstance.newLegend);
          delete chartInstance.newLegend;
        }
      },
      afterEvent: function(chartInstance, e) {
        var legend = chartInstance.newLegend;
        if (legend) {
          legend.handleEvent(e);
        }
      }
    });
    

    底部填充

    也没有选项来控制它,所以我们也必须内置它。由于我们在这里处理比例,所以最好的方法是扩展“类别”比例并添加逻辑来处理比例paddingTop 选项。通读源代码后,我们需要覆盖draw() 函数来执行此操作。这是相关代码(完整实现请参见我的示例)。

    // ...
    if (isHorizontal) {
      if (options.position === 'bottom') {
        // bottom
        textBaseline = !isRotated? 'top':'middle';
        textAlign = !isRotated? 'center': 'right';
        labelY = me.top + tl + me.options.paddingTop;
      } else {
        // top
        textBaseline = !isRotated? 'bottom':'middle';
        textAlign = !isRotated? 'center': 'left';
        labelY = me.bottom - tl;
      }
    }
    // ...
    

    这是一个codepen example,展示了所有这些。

    【讨论】:

    • 您好!谢谢,这行得通。对于这么小的事情需要这么多代码,我感到非常震惊。我认为这个选项应该在默认包中。非常感谢您的宝贵时间。
    • 我同意。如果我能解决它,也许我会向 chart.js 家伙提交一个请求。
    • 感谢您的示例。只是几个问题。 getBoxWidth 函数有什么用?那么位置的图例呢:'bottom'('left'/'right')。这个例子似乎总是将图例放在顶部,即使图例位置设置为其他值。
    【解决方案2】:

    编辑:这不提供图表内的填充(这是这个问题最初的目的),而是在图表周围添加一个填充

    对于那些仍在寻找可能在谷歌搜索中结束的人(如我),它似乎已在更高版本中得到解决:https://www.chartjs.org/docs/latest/configuration/layout.html#padding

    let chart = new Chart(ctx, {
      type: 'line',
      data: data,
      options: {
        layout: {
            padding: {
                left: 50,
                right: 0,
                top: 0,
                bottom: 0
            }
        }
      }
    });
    

    【讨论】:

    • 控制“完整”图表周围的填充,所以不幸的是,这只在图例之外添加了空间,而不是在图例和图表之间。
    • 哦..你是完全正确的..我在解决我自己的问题时误读了这个问题,这略有不同。
    • 查看答案以修改 lengend 填充:stackoverflow.com/questions/42585861/…
    • 唷,你救了我的命,我用不同的关键字搜索了将近 3 个小时,尝试了不同的技术但失败了,最后我找到了。我在饼图的数据标签上添加了偏移量,它是从顶部和底部裁剪的,现在它已固定。
    • 如果这不能回答问题,请考虑将其删除,因为您只会造成混乱。
    【解决方案3】:

    对于 ChartJs 版本 3,您可以删除 tickLenght:

    scales: {
          x: {
            grid: {
              tickWidth:0,
              tickLength: 0,
          },
          y: {
            grid: {
              tickWidth:0,
              tickLength: 0,
          },
        ...
        }
    

    【讨论】:

      【解决方案4】:

      这不会在图表和图例之间添加填充,但上面的代码似乎不再适用于 Chart.js v3 及更高版本。这是一个解决方法插件,它填充 y 轴以确保标签(仅使用 chartjs-plugin-datalabels 检查)落在图表内。

      var pluginAutoAdjustRangeY = {
         id: 'autoAdjustRangeY',
         beforeInit: function(chartInstance) {
            var legendOpts = chartInstance.options.legend;
            if (legendOpts) {
               chartInstance.legend.afterFit = function(){
                  var max = 0;
                  var min = 0;
      
                  var datasets = chartInstance.data.datasets.length
                  for(let i = 0; i < datasets; i++){
                     var points = chartInstance.data.datasets[i].data.length
                     for(let p = 0; p < points; p++){
                        var v = parseFloat(chartInstance.data.datasets[i].data[p]);
                        if(v > max) max = v; 
                        if(v < min) min = v; 
                     }           
                  }
                 
                  var range = parseInt((max - min) * ((chartInstance.options.legend.padding) || 0));
                  chartInstance.options.scales.y.max = parseInt(max + range);
                  if(min !== 0) chartInstance.options.scales.y.min = parseInt(min - range);
               }
            }
         },   
         beforeLayout: function(chartInstance) { //2
            if(chartInstance.options.legend){
               if(chartInstance.legend.afterFit){
                  chartInstance.legend.afterFit();
               }
            }
         },
      };
      
      

      并且可以这样使用:

      options: {
         legend: {
            padding: 0.15,  //percentage of Y range to pad ends of axis
         }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-16
        • 2011-11-02
        • 2017-08-21
        • 1970-01-01
        • 2012-08-25
        • 2014-04-13
        • 1970-01-01
        相关资源
        最近更新 更多