在chart.js中显示柱状图上的值

49
请参考这个 Fiddle: https://jsfiddle.net/4mxhogmd/1/ 我正在使用 chart.js。如果你在 fiddle 中查看,你会注意到某些情况下顶部的值没有正确显示(超出了画布)。在研究过程中,我找到了这个链接:如何在 Chart.js 上显示数据值
但是,在这里,他们还将工具提示用于相同的情况来调整条形内部的文本。我不想要这个。
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ["2 Jan", "9 Jan", "16 Jan", "23 Jan", "30 Jan", "6 Feb", "13 Feb"],
        datasets: [{
            data: [6, 87, 56, 15, 88, 60, 12],
            backgroundColor: "#4082c4"
        }]
    },
    options: {
        "hover": {
            "animationDuration": 0
        },
        "animation": {
            "duration": 1,
            "onComplete": function () {
                var chartInstance = this.chart,
                ctx = chartInstance.ctx;

                ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
                ctx.textAlign = 'center';
                ctx.textBaseline = 'bottom';

                this.data.datasets.forEach(function (dataset, i) {
                    var meta = chartInstance.controller.getDatasetMeta(i);
                    meta.data.forEach(function (bar, index) {
                        var data = dataset.data[index];                            
                        ctx.fillText(data, bar._model.x, bar._model.y - 5);
                    });
                });
            }
        },
        legend: {
            "display": false
        },
        tooltips: {
            "enabled": false
        },
        scales: {
            yAxes: [{
                display: false,
                gridLines: {
                    display : false
                },
                ticks: {
                    display: false,
                    beginAtZero:true
                }
            }],
            xAxes: [{
                gridLines: {
                    display : false
                },
                ticks: {
                    beginAtZero:true
                }
            }]
        }
    }
});

我希望的是在所有情况下只显示顶部的值。


如果您添加了第二个数据对象,然后通过第一个数据对象过滤数据,那么第二个数据对象的值仍然可见。是否有解决此问题的方法? - abautista
9个回答

62

我从myChart定义的数据中提取了数据,这样我就可以从数据集中提取最大值。然后在yAxes内部,您可以将最大刻度设置为数据集最大值加上10。这可以确保图表中的顶部柱不会超出画布边缘并显示其值。

var ctx = document.getElementById("myChart");
debugger;
var data = {
  labels: ["2 Jan", "9 Jan", "16 Jan", "23 Jan", "30 Jan", "6 Feb", "13 Feb"],
  datasets: [{
    data: [150, 87, 56, 50, 88, 60, 45],
    backgroundColor: "#4082c4"
  }]
}
var myChart = new Chart(ctx, {
  type: 'bar',
  data: data,
  options: {
    "hover": {
      "animationDuration": 0
    },
    "animation": {
      "duration": 1,
      "onComplete": function() {
        var chartInstance = this.chart,
          ctx = chartInstance.ctx;

        ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
        ctx.textAlign = 'center';
        ctx.textBaseline = 'bottom';

        this.data.datasets.forEach(function(dataset, i) {
          var meta = chartInstance.controller.getDatasetMeta(i);
          meta.data.forEach(function(bar, index) {
            var data = dataset.data[index];
            ctx.fillText(data, bar._model.x, bar._model.y - 5);
          });
        });
      }
    },
    legend: {
      "display": false
    },
    tooltips: {
      "enabled": false
    },
    scales: {
      yAxes: [{
        display: false,
        gridLines: {
          display: false
        },
        ticks: {
          max: Math.max(...data.datasets[0].data) + 10,
          display: false,
          beginAtZero: true
        }
      }],
      xAxes: [{
        gridLines: {
          display: false
        },
        ticks: {
          beginAtZero: true
        }
      }]
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.js"></script>
<canvas id="myChart" width="100" height="100"></canvas>


1
这不是一个正确的答案。如果值太大,这将无法工作。但是通过进行一些更改,我仍然可以实现我想要的结果。非常感谢。 - Kiran Shinde
@Kenny 和其他人:请查看 DavidX的答案。它提供了一个能够完成任务的优雅插件。 - J.Baoby
谢谢,但是不能处理负数条。 - crg
1
chart.js 第3版有很多升级,但大部分都是我所需要的。例如,bar._model.x 不再存在,现在是 bar.x。而 chartInstance.controller.getDatasetMeta(i) 也改为 chartInstance.getDatasetMeta(i)。 - workaholic

43

我发现最快的方法是使用这个插件:https://github.com/chartjs/chartjs-plugin-datalabels

您可以通过将插件导入到js文件中来简单地向图表添加标签,例如:

import 'chartjs-plugin-datalabels'

如果您想在顶部(全局)应用这些值,请在代码中设置以下选项:

Chart.defaults.global.plugins.datalabels.anchor = 'end';
Chart.defaults.global.plugins.datalabels.align = 'end';

更多选项可以在这里找到:https://chartjs-plugin-datalabels.netlify.com/options.html


8
这是一个绝妙的回答!如果你想要聚合堆叠图表,可以查看这个链接:https://github.com/chartjs/chartjs-plugin-datalabels/issues/16 - sambecker
1
有人知道那个库是否处理额外的空间以在必要时显示标签吗?我通过增加原始大小的10%来实现了这一点,但如果该库默认执行此操作,则会更好。const maxValue = Math.max(...data.datasets[0].data) options.scales.yAxes[0].ticks.suggestedMax = maxValue * 1.1 - Sheldon Oliveira

20

这里有一个更强大的解决方案,可以在坐标轴高度接近柱形图高度的情况下,在柱形内部显示数据点的值。换句话说,如果文本将超出画布可见区域,这将在柱形上方和/或下方显示该值。

Chart.plugins.register({
  afterDraw: function(chartInstance) {
    if (chartInstance.config.options.showDatapoints) {
      var helpers = Chart.helpers;
      var ctx = chartInstance.chart.ctx;
      var fontColor = helpers.getValueOrDefault(chartInstance.config.options.showDatapoints.fontColor, chartInstance.config.options.defaultFontColor);

      // render the value of the chart above the bar
      ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily);
      ctx.textAlign = 'center';
      ctx.textBaseline = 'bottom';
      ctx.fillStyle = fontColor;

      chartInstance.data.datasets.forEach(function (dataset) {
        for (var i = 0; i < dataset.data.length; i++) {
          var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
          var scaleMax = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._yScale.maxHeight;
          var yPos = (scaleMax - model.y) / scaleMax >= 0.93 ? model.y + 20 : model.y - 5;
          ctx.fillText(dataset.data[i], model.x, yPos);
        }
      });
    }
  }
});

您可以通过在图表选项中添加以下属性来启用插件。

showDatapoints: true,

我不想要这个。我希望值始终仅在顶部显示。无论如何,谢谢。 - Kiran Shinde
这对其他图表有效吗?甜甜圈、雷达等。尝试使用它,但它不起作用。 - Yorki Bonilla
如果在fillText中考虑数据集循环变量(function(dataset, eachLoopIndex)),并且柱状图是堆叠版本,则同一列上的两个值不会相交,如下所示:ctx.fillText(dataset.data[i], model.x, y_pos-eachLegendIndex*10); - gawkface
如何增加在条形上方可见的文本大小? - Vaibhav Grover
警告:在画布和图表绘制期间,后端引擎会因各种事件(如页面重新加载、显示方向更改、画布单击等)而多次触发afterDraw()回调。请使用console.log()进行验证。如果您的操作消耗资源较多,则可能需要避免使用afterDraw()回调。 - Panini Luncher

5

在我的情况下,这起作用了。(如下所示的插件部分)

options: {
    responsive: true,
    scales: {
      yAxes: [{
        ticks: {
          beginAtZero: true,
        }
      }]
    },
    plugins: {
      datalabels: {
        anchor: 'end',
        align: 'top',
        formatter: Math.round,
        font: {
          weight: 'bold'
        }
      }
    }
  }

愉快的编码。


我完全按照你的代码粘贴,但它根本不起作用。它能正常工作吗? - code-8
是的,这是可行的代码。什么意思是完全不工作??你有收到任何错误吗?? - Bandham Manikanta
请确保您已安装“chartjs-plugin-datalabels”插件(package.json)。您可以参考此文档以获取更多详细信息。 https://chartjs-plugin-datalabels.netlify.com/guide/getting-started.html#configuration - Bandham Manikanta

4

尝试这些选项:

animation: {
  duration: 1,
  onComplete: function({ chart }) {
    const ctx = chart.ctx;

    chart.config.data.datasets.forEach(function(dataset, i) {
      const meta = chart.getDatasetMeta(i);

      meta.data.forEach(function(bar, index) {
        const data = dataset.data[index];

        ctx.fillText(data, bar.x, bar.y - 5);
      });
    });
  }
}

2

您可以在图表选项中添加一些padding-top

layout: {
    padding: {
        left: 0,
        right: 0,
        top: 30,
        bottom: 0
    }
 },

1
你可以考虑使用插件,在每个条形图的顶部显示数据值。
plugins: {
afterDatasetsDraw: function (context, easing) {
 var ctx = context.chart.ctx;
   context.data.datasets.forEach(function (dataset) {
     for (var i = 0; i < dataset.data.length; i++) {
        if (dataset.data[i] != 0) {
          var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
            var textY = model.y + (dataset.type == "line" ? -3 : 15); 
              ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily);
              ctx.textAlign = 'start';
              ctx.textBaseline = 'middle';
              ctx.fillStyle = dataset.type == "line" ? "black" : "black";
              ctx.save();
              ctx.translate(model.x, textY-15);
              ctx.rotate(4.7);
              ctx.fillText(dataset.data[i], 0, 0);
              ctx.restore();
             }
           }
         });
       }
    }

现场代码:链接


0

这在我的情况下有效,但它会在条形图中间显示值。

chart.chart.config.options.animation["onComplete"] =  function () {
        var ctx = chart.chart.ctx;
        ctx.font = '22px "Helvetica Neue", Helvetica, Arial, sans-serif';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'bottom';

        this.data.datasets.forEach(function (dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
                    scale_max = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._yScale.maxHeight;
                ctx.fillStyle = '#444';
                var y_pos = model.y + 50;
                if ((scale_max - model.y) / scale_max >= 0.5)
                    y_pos = model.y + 20;
                ctx.fillText(dataset.data[i], model.x, y_pos);    
            }
        });                
    }

我们如何使用这段代码?我们在哪里传递它?配置?选项? - code-8

-1
var ctx = document.getElementById("myChart");
debugger;
var data = {
  labels: ["2 Jan", "9 Jan", "16 Jan", "23 Jan", "30 Jan", "6 Feb", "13 Feb"],
  datasets: [{
    data: [150, 87, 56, 50, 88, 60, 45],
    backgroundColor: "#4082c4"
  }]
}
var myChart = new Chart(ctx, {
  type: 'bar',
  data: data,
  options: {
    "hover": {
      "animationDuration": 0
    },
    "animation": {
      "duration": 1,
      "onComplete": function() {
        var chartInstance = this.chart,
          ctx = chartInstance.ctx;

        ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
        ctx.textAlign = 'center';
        ctx.textBaseline = 'bottom';

        this.data.datasets.forEach(function(dataset, i) {
          var meta = chartInstance.controller.getDatasetMeta(i);
          meta.data.forEach(function(bar, index) {
            var data = dataset.data[index];
            ctx.fillText(data, bar._model.x, bar._model.y - 5);
          });
        });
      }
    },
    legend: {
      "display": false
    },
    tooltips: {
      "enabled": false
    },
    scales: {
      yAxes: [{
        display: false,
        gridLines: {
          display: false
        },
        ticks: {
          max: Math.max(...data.datasets[0].data) + 10,
          display: false,
          beginAtZero: true
        }
      }],
      xAxes: [{
        gridLines: {
          display: false
        },
        ticks: {
          beginAtZero: true
        }
      }]
    }
  }
});

1
请不要只提供代码片段,最好能够解释您的代码。如何撰写一个好的答案? - The Myth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接