ChartJS:datalabels:在饼图中显示百分比数值

53

我有一个带有四个标签的饼图:

var data = [{
    data: [50, 55, 60, 33],
    labels: ["India", "China", "US", "Canada"],
    backgroundColor: [
        "#4b77a9",
        "#5f255f",
        "#d21243",
        "#B27200"
    ],
    borderColor: "#fff"
}];

我想使用chartjs-plugin-datalabels插件在每个饼图块中显示百分比值,以下是代码:

formatter: (value, ctx) => {

        let datasets = ctx.chart.data.datasets;

        if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
            let sum = 0;
            datasets.map(dataset => {
                sum += dataset.data[ctx.dataIndex];
            });
            let percentage = Math.round((value / sum) * 100) + '%';
            return percentage;
        } else {
            return percentage;
        }
    },
    color: '#fff',
}

我得到了所有馅饼块的100%价值,而不是各自的百分比。

这是JSFiddle(https://jsfiddle.net/kingBethal/a1Lvn4eb/7/)。


1
我知道 chartjs-plugin-labels 可以实现这个功能,但是我更喜欢 chartjs-plugin-datalabels,因为我在其他图表函数中依赖于这个插件。 - theKing
10个回答

74

更新的 Fiddle,精确到小数点后2位。

您没有计算总和,而是仅为每个值存储当前值。

这是可工作的 Fiddle: https://jsfiddle.net/a1Lvn4eb/55/

var data = [{
    data: [50, 55, 60, 33],
    labels: ["India", "China", "US", "Canada"],
    backgroundColor: [
        "#4b77a9",
        "#5f255f",
        "#d21243",
        "#B27200"
    ],
    borderColor: "#fff"
}];

var options = {
    tooltips: {
        enabled: false
    },
    plugins: {
        datalabels: {
            formatter: (value, ctx) => {
                let sum = 0;
                let dataArr = ctx.chart.data.datasets[0].data;
                dataArr.map(data => {
                    sum += data;
                });
                let percentage = (value*100 / sum).toFixed(2)+"%";
                return percentage;
            },
            color: '#fff',
        }
    }
};

var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
    type: 'pie',
    data: {
        datasets: data
    },
    options: options
});

已按要求更新 :) - Hiteshdua1
如何在图表上隐藏数值为0%且特定标签(例如:中国)的数据? - theKing
图表块中的标签为什么没有居中显示? - Usama
这里有一个小问题。toFixed() 可能不可靠。现在最好使用 Intl.NumberFormat() - D-Marc
我需要添加 sum += parseInt(data); 才能使其正常工作。 - Frizzant
显示剩余3条评论

12
这是用于 chart.js >= 3.x 和 chartjs-plugin-datalabels >= 2.x 的更新。
chartjs-plugin-datalabels 插件现在不再自动注册文档)。
您需要手动注册它。 为所有图表注册插件。
Chart.register(ChartDataLabels)

或仅限于特定图表。
var chart = new Chart(ctx, {
  plugins: [ChartDataLabels],
  options: {
    // ...
  }
})

以下是呈现饼图的代码:

var data = [{
  data: [50, 55, 60, 33],
  backgroundColor: [
    "#4b77a9",
    "#5f255f",
    "#d21243",
    "#B27200"
  ],
  borderColor: "#fff"
}];

var options = {
  tooltips: {
    enabled: false
  },
  plugins: {
    datalabels: {
      formatter: (value, ctx) => {
        const datapoints = ctx.chart.data.datasets[0].data
         const total = datapoints.reduce((total, datapoint) => total + datapoint, 0)
        const percentage = value / total * 100
        return percentage.toFixed(2) + "%";
      },
      color: '#fff',
    }
  }
};

var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
  type: 'pie',
  data: {
  labels: ['India', 'China', 'US', 'Canada'],
    datasets: data
  },
  options: options,
  plugins: [ChartDataLabels],
});
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.0.0/chartjs-plugin-datalabels.min.js"></script>
  <style>
.container {
width: 40%;
margin: 5px auto;
}
  </style>
  <div class="container">
<canvas id="pie-chart"></canvas>
  </div>


1
你的回答对我非常有用 - 但问题是我使用了一个侧脚本,它以类似于 [{"student_name":"APNET","marks":"45"},{"student_name":"ARZ","marks":"55"}] 的格式给出最终输出 - 所以我没有一个简单的数据:[45, 55](这个可以工作!),但我想要的是 ["45", "55"]data: marks 下 - 结果计算完全错误。是否可能在 formatter: (value, ctx) => { 的开始处进行 str_replace,尝试从 ["45", "55"] 中删除双引号,我假设 data: marks 传递给 const datapoints = ctx.chart.data.datasets[0].data - Serge

10
我喜欢在被接受的答案中增加一点内容,ctx.chart.data.datasets[0].data始终会给出整个数据,即使您通过单击图例过滤了一些数据,这意味着即使您筛选掉某些国家,您也将始终获得同样的百分比。我使用了context.dataset._meta[0].total来获取过滤后的总数。以下是可工作的代码片段:

var data = [{
  data: [50, 55, 60, 33],
  backgroundColor: [
    "#4b77a9",
    "#5f255f",
    "#d21243",
    "#B27200"
  ],
  borderColor: "#fff"
}];

var options = {
  tooltips: {
    enabled: true
  },
  plugins: {
    datalabels: {
      formatter: (value, ctx) => {

        let sum = ctx.dataset._meta[0].total;
        let percentage = (value * 100 / sum).toFixed(2) + "%";
        return percentage;


      },
      color: '#fff',
    }
  }
};


var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
  type: 'pie',
  data: {
  labels: ['India', 'China', 'US', 'Canada'],
    datasets: data
  },
  options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@0.7.0"></script>
<canvas id="pie-chart"></canvas>


谢谢 - 但这实际上不需要jquery,对吗? - ashleedawg
是的,它不需要 jQuery。 - Muhammad Ashhar Hasan
1
显然,在版本chartjs v3.7.0和chartjs-plugin-datalabels v2.0.0中,它在这里不起作用。_meta不是一个有效的变量(再也不是了?)。尝试找出还有什么其他方法可以工作。背景:我还想根据可见的百分比更新百分比。 - Manuel Senfft
我想出了一个丑陋的猴子补丁解决方法,我会将其作为新答案发布,因为这个评论系统不适合。抱歉。不过我会在这个帖子里发布它!--> https://dev59.com/cVQK5IYBdhLWcg3wUOZ5#71068131 - Manuel Senfft

7
问题在于你如何计算总和。请看下面。 演示
 var data = [{
   data: [50, 55, 60, 33],
   labels: ["India", "China", "US", "Canada"],
   backgroundColor: [
     "#4b77a9",
     "#5f255f",
     "#d21243",
     "#B27200"
   ],
   borderColor: "#fff"
 }];

 var options = {
   tooltips: {
     enabled: false
   },
   plugins: {
     datalabels: {
       formatter: (value, ctx) => {

         let datasets = ctx.chart.data.datasets;

         if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
           let sum = datasets[0].data.reduce((a, b) => a + b, 0);
           let percentage = Math.round((value / sum) * 100) + '%';
           return percentage;
         } else {
           return percentage;
         }
       },
       color: '#fff',
     }
   }
 };


 var ctx = document.getElementById("pie-chart").getContext('2d');
 var myChart = new Chart(ctx, {
   type: 'pie',
   data: {
     datasets: data
   },
   options: options
 });

1
谢谢 Michael,这个也可以。我更喜欢 @Hiteshdua1 的答案,因为它还显示了2位小数,这正是我需要的。 - theKing
抱歉,在最新版本的ChartJS 3.0中,它对我来说没有起作用。我也更新了插件。 - marti_

5
您可以使用数组 reducer 来执行百分比计算并显示在 tooltip 中。
  tooltips: {
    callbacks: {
      label: function (tooltipItem, data) {
        try {
          let label = ' ' + data.labels[tooltipItem.index] || '';

          if (label) {
            label += ': ';
          }

          const sum = data.datasets[0].data.reduce((accumulator, curValue) => {
            return accumulator + curValue;
          });
          const value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];

          label += Number((value / sum) * 100).toFixed(2) + '%';
          return label;
        } catch (error) {
          console.log(error);
        }
      }
    }
  }

我不得不在reduce方法中做一个小改变: 返回parseInt(accumulator) + parseInt(curValue); - Faiyaz Alam

3

正如一些人所提到的那样,context.dataset._meta[0].total在chart.js 3及以上版本中似乎已被删除并且不再起作用。相反,我使用了context.chart.getDatasetMeta(0).total,这对我有效——它展示了饼图中的百分比值,并且当单击图例时,该值根据过滤的总计数得到更新。

      options:{ 
        plugins:{         
          datalabels: {
            color: 'white',
            formatter: function(value, context) {                  
              return Math.round(value/context.chart.getDatasetMeta(0).total * 100) + "%" ;
            }
          }
        }
      }

3
我正在使用ChartJS v3.7.0和chartjs-plugin-datalabels v2.0.0以及此答案:https://dev59.com/cVQK5IYBdhLWcg3wUOZ5#59403435。然而,对我来说并不起作用,因为我只想在隐藏元素时保持百分比的最新状态。似乎ctx.dataset._meta不再可用。我想到了一些丑陋的猴子补丁。
formatter: function(value, context) {
    var hiddens = context.chart._hiddenIndices;
    var total = 0;
    var datapoints = context.dataset.data;
    datapoints.forEach((val, i) => {
        if (hiddens[i] != undefined) {
            if (!hiddens[i]) {
                total += val;
            }
        } else {
            total += val;
        }
    });
    var percentage = (value / total * 100).toFixed(2) + '%';
    var out = context.chart.data.labels[context.dataIndex] + '\n' + percentage;
    return out;
}

希望这能帮助到任何人。干杯。

在自己花了几个小时找到答案后,看到这个回答真是太棒了! - Tomás Gomez Pizarro

2

显然,ChartDataLabels现在是从另一个包chartjs-plugin-datalabels导出的。 我使用 npm i chartjs-plugin-datalabels安装了该软件包,然后修改了我的代码如下。

import React from "react";
import ChartDataLabels from "chartjs-plugin-datalabels";
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
import { Pie } from "react-chartjs-2";

ChartJS.register(ArcElement, Tooltip, Legend, ChartDataLabels);

export default function PieChart() {
  return (
    <>
      <Pie
        options={{
          responsive: true,
          maintainAspectRatio: false,
          plugins: {
            legend: { display: false },
            tooltip: false,
            datalabels: {
              formatter: (value, ctx) => {
                let sum = 0;
                let dataArr = ctx.chart.data.datasets[0].data;
                dataArr.map((data) => {
                  sum += data;
                });
                let percentage = ((value * 100) / sum).toFixed(2) + "%";
                return percentage;
              },
              color: "#FFF",
            },
          },
          onHover: function (e) {
            e.native.target.style.cursor = "pointer";
          },
          rotation: 270,
          events: ["click"],
          onClick: function (event, element) {
            console.log(element[0].index);
          },
        }}
        data={{
          labels: ["Above 60%", "Below 60%"],
          datasets: [
            {
              data: [20, 10],
              backgroundColor: ["#3A925D", "#FD7E14"],
              borderWidth: 0,
            },
          ],
        }}
      />
    </>
  );
}

结果 在此输入图片描述


1
如果您使用Node.js: 请按以下步骤操作:
  1. 运行命令npm install chartjs-plugin-datalabels
  2. 导入ChartDataLabels:import ChartDataLabels from 'chartjs-plugin-datalabels';
  3. 注册ChartDataLabels插件:Chart.register(...registerables, ChartDataLabels);
  4. 从上面的注释中复制代码...

https://chartjs-plugin-datalabels.netlify.app/guide/#table-of-contents


-1
使用一个本地的 999 字节的 JavaScript Web 组件:https://pie-meister.github.io/

<script src="https://pie-meister.github.io/PieMeister.min.js"></script>

<pie-chart pull="10" pulltext="100">
  <style>
    text {
      font: 3em Arial;
      text-anchor: middle;
      fill: white;
    }
  </style>
  <slice size="25%" stroke="#4b77a9" pull>India $size</slice>
  <slice size="28%" stroke="#5f255f" pull>China $size</slice>
  <slice size="30%" stroke="#d21243" pull>US $size</slice>
  <slice size="17%" stroke="#B27200" pull>Canada $size</slice>
</pie-chart>

JSWC

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