How to display pie chart data values of each slice in chart.js
Asked Answered
R

8

41

I am using Chart.js for drawing pie chart in my php page.I found tooltip as showing each slice values.
enter image description here

But I wish to display those values like below image. enter image description here

I do not know how to do this with chart.js.

Please help me.

My Javascript code:

function drawPie(canvasId,data,legend){
    var ctx = $("#pie-canvas-" + canvasId).get(0).getContext("2d");

    var piedata = [];
    $.each(data,function(i,val){
        piedata.push({value:val.count,color:val.color,label:val.status});
    });
    var options =
    {
        tooltipTemplate: "<%= Math.round(circumference / 6.283 * 100) %>%",
    }
    var pie = new Chart(ctx).Pie(piedata,options);
    if(legend)document.getElementById("legend").innerHTML = pie.generateLegend();
}

php code:

printf('<table><tr>');
echo '<td style="text-align: right;"><canvas id="pie-canvas-'
    . $canvasId
    . '" width="256" height="256" ></canvas></td><td style="text-align: left;width:360px;height:auto" id="legend" class="chart-legend"></td></tr></table>';

     echo '<script type="text/javascript">drawPie('
    . $canvasId
    . ', '
    . $data3
    .', '
    . $legend
    . ');</script>';

enter image description here

Rh answered 27/10, 2015 at 8:48 Comment(6)
You want that 46% to be used or it represent something?Eucharis
No.I want to display that values like in 2nd image.That is I do not want it to be displayed in tooltip.Simply within the chart.Rh
I don't think the PHP code is needed here, I would just add the result of the PHP file to the question to allow an example snippet or fiddle. Also you can just use ctx.fillText() to do that, I don't think chart.js has direct support for doing that, but I may be wrong.Anastase
You could also add a custom tool-tip that simply shows the text.Anastase
@Spencer.I tried with ctx.fillText() and custom tool-tip.But still I didnt get.Can you please send some snippets from your try.?Rh
@Rh Sure, I gave a solution below using fillText().Anastase
A
23

From what I know I don't believe that Chart.JS has any functionality to help for drawing text on a pie chart. But that doesn't mean you can't do it yourself in native JavaScript. I will give you an example on how to do that, below is the code for drawing text for each segment in the pie chart:

function drawSegmentValues()
{
    for(var i=0; i<myPieChart.segments.length; i++) 
    {
        // Default properties for text (size is scaled)
        ctx.fillStyle="white";
        var textSize = canvas.width/10;
        ctx.font= textSize+"px Verdana";

        // Get needed variables
        var value = myPieChart.segments[i].value;
        var startAngle = myPieChart.segments[i].startAngle;
        var endAngle = myPieChart.segments[i].endAngle;
        var middleAngle = startAngle + ((endAngle - startAngle)/2);

        // Compute text location
        var posX = (radius/2) * Math.cos(middleAngle) + midX;
        var posY = (radius/2) * Math.sin(middleAngle) + midY;

        // Text offside to middle of text
        var w_offset = ctx.measureText(value).width/2;
        var h_offset = textSize/4;

        ctx.fillText(value, posX - w_offset, posY + h_offset);
    }
}

A Pie Chart has an array of segments stored in PieChart.segments, we can look at the startAngle and endAngle of these segments to determine the angle in between where the text would be middleAngle. Then we would move in that direction by Radius/2 to be in the middle point of the chart in radians.

In the example above some other clean-up operations are done, due to the position of text drawn in fillText() being the top right corner, we need to get some offset values to correct for that. And finally textSize is determined based on the size of the chart itself, the larger the chart the larger the text.

Fiddle Example


With some slight modification you can change the discrete number values for a dataset into the percentile numbers in a graph. To do this get the total value of the items in your dataset, call this totalValue. Then on each segment you can find the percent by doing:

Math.round(myPieChart.segments[i].value/totalValue*100)+'%';

The section here myPieChart.segments[i].value/totalValue is what calculates the percent that the segment takes up in the chart. For example if the current segment had a value of 50 and the totalValue was 200. Then the percent that the segment took up would be: 50/200 => 0.25. The rest is to make this look nice. 0.25*100 => 25, then we add a % at the end. For whole number percent tiles I rounded to the nearest integer, although can can lead to problems with accuracy. If we need more accuracy you can use .toFixed(n) to save decimal places. For example we could do this to save a single decimal place when needed:

var value = myPieChart.segments[i].value/totalValue*100;
if(Math.round(value) !== value)
    value = (myPieChart.segments[i].value/totalValue*100).toFixed(1);
value = value + '%';

Fiddle Example of percentile with decimals

Fiddle Example of percentile with integers

Anastase answered 27/10, 2015 at 18:53 Comment(9)
@spencer...perfect!!..Thanks ..but here var value = myPieChart.segments[i].value; I wish the value would be in percentage of pie chart..Can you tell me how to do here..Rh
@Rh The value is explicitly defined in the JS Object as a number, and the pie chart looks at those values to determine how much of the chart they take up. So you can get that by doing currentValue/totalValue to get the percentile (ex: =>0.50). Times that by 100 =>50, then add a "%" on the end. Example here.Anastase
If the values are low .0.8% and 1%..If these values are nearby both values are override.it can not be readable.I tried with modifying angle,but totally confusing.How would you do?..I added the screenshot above.Rh
@Rh One way that could help is to change how far the text is drawn on the canvas (determined by (radius/2) in the code) for small values. For small values (say less than 5%), you may want to draw them on the edge or past the edge of the drawn graph, and along with that display force the display to be an integer. I'll give an example tomorrow.Anastase
Wow - very impressive answer; if it works for me, I'm going to Bountify it myself, because I've searched high and low for this answer.Memoried
If you feel like it and have time, please czech out #39646820 (will be bountified for 500 points ASAP)Memoried
This works perfectly, but I didn't got the sin and cos part. Can anybody explain why we had to multiply r/2 with sin of middle angle?Ferule
@Amanjangra Here is a diagram visually showing each item (Made in MS Paint just now). This is is a unit circle with a radius of radius, cos(angle) tells us where to go in the X direction and sin(angle) where to go in the Y direction. The radius/2 is the actual scalar on how far to move in that direction. 0 would be in the center of the circle, r/2 would be in the middle of the circle, and r would be at the very edges of the circle.Anastase
@SpencerWieczorek Thanks, just needed to actully see it. Thanks again.Ferule
P
76

For Chart.js 2.0 and up, the Chart object data has changed. For those who are using Chart.js 2.0+, below is an example of using HTML5 Canvas fillText() method to display data value inside of the pie slice. The code works for doughnut chart, too, with the only difference being type: 'pie' versus type: 'doughnut' when creating the chart.

Script:

Javascript

var data = {
    datasets: [{
        data: [
            11,
            16,
            7,
            3,
            14
        ],
        backgroundColor: [
            "#FF6384",
            "#4BC0C0",
            "#FFCE56",
            "#E7E9ED",
            "#36A2EB"
        ],
        label: 'My dataset' // for legend
    }],
    labels: [
        "Red",
        "Green",
        "Yellow",
        "Grey",
        "Blue"
    ]
};

var pieOptions = {
  events: false,
  animation: {
    duration: 500,
    easing: "easeOutQuart",
    onComplete: function () {
      var ctx = this.chart.ctx;
      ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
      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,
              total = dataset._meta[Object.keys(dataset._meta)[0]].total,
              mid_radius = model.innerRadius + (model.outerRadius - model.innerRadius)/2,
              start_angle = model.startAngle,
              end_angle = model.endAngle,
              mid_angle = start_angle + (end_angle - start_angle)/2;

          var x = mid_radius * Math.cos(mid_angle);
          var y = mid_radius * Math.sin(mid_angle);

          ctx.fillStyle = '#fff';
          if (i == 3){ // Darker text color for lighter background
            ctx.fillStyle = '#444';
          }
          var percent = String(Math.round(dataset.data[i]/total*100)) + "%";      
          //Don't Display If Legend is hide or value is 0
          if(dataset.data[i] != 0 && dataset._meta[0].data[i].hidden != true) {
            ctx.fillText(dataset.data[i], model.x + x, model.y + y);
            // Display percent in another line, line break doesn't work for fillText
            ctx.fillText(percent, model.x + x, model.y + y + 15);
          }
        }
      });               
    }
  }
};

var pieChartCanvas = $("#pieChart");
var pieChart = new Chart(pieChartCanvas, {
  type: 'pie', // or doughnut
  data: data,
  options: pieOptions
});

HTML

<canvas id="pieChart" width=200 height=200></canvas>

jsFiddle

Paschasia answered 5/8, 2016 at 20:51 Comment(3)
I realise this is an old answer, but would you be able to explain how I could swap out the percentage for the label? So the data is underneath the label on each slice? ThanksGlottal
Nevermind, was able to get the label with var label = data.labels[i]; if anyone is wondering in the futureGlottal
I have other chart in same Javascript. So what will be the ctx then?Blim
T
28

I found an excellent Chart.js plugin that does exactly what you want: https://github.com/emn178/Chart.PieceLabel.js

Taluk answered 21/3, 2017 at 16:33 Comment(0)
A
23

From what I know I don't believe that Chart.JS has any functionality to help for drawing text on a pie chart. But that doesn't mean you can't do it yourself in native JavaScript. I will give you an example on how to do that, below is the code for drawing text for each segment in the pie chart:

function drawSegmentValues()
{
    for(var i=0; i<myPieChart.segments.length; i++) 
    {
        // Default properties for text (size is scaled)
        ctx.fillStyle="white";
        var textSize = canvas.width/10;
        ctx.font= textSize+"px Verdana";

        // Get needed variables
        var value = myPieChart.segments[i].value;
        var startAngle = myPieChart.segments[i].startAngle;
        var endAngle = myPieChart.segments[i].endAngle;
        var middleAngle = startAngle + ((endAngle - startAngle)/2);

        // Compute text location
        var posX = (radius/2) * Math.cos(middleAngle) + midX;
        var posY = (radius/2) * Math.sin(middleAngle) + midY;

        // Text offside to middle of text
        var w_offset = ctx.measureText(value).width/2;
        var h_offset = textSize/4;

        ctx.fillText(value, posX - w_offset, posY + h_offset);
    }
}

A Pie Chart has an array of segments stored in PieChart.segments, we can look at the startAngle and endAngle of these segments to determine the angle in between where the text would be middleAngle. Then we would move in that direction by Radius/2 to be in the middle point of the chart in radians.

In the example above some other clean-up operations are done, due to the position of text drawn in fillText() being the top right corner, we need to get some offset values to correct for that. And finally textSize is determined based on the size of the chart itself, the larger the chart the larger the text.

Fiddle Example


With some slight modification you can change the discrete number values for a dataset into the percentile numbers in a graph. To do this get the total value of the items in your dataset, call this totalValue. Then on each segment you can find the percent by doing:

Math.round(myPieChart.segments[i].value/totalValue*100)+'%';

The section here myPieChart.segments[i].value/totalValue is what calculates the percent that the segment takes up in the chart. For example if the current segment had a value of 50 and the totalValue was 200. Then the percent that the segment took up would be: 50/200 => 0.25. The rest is to make this look nice. 0.25*100 => 25, then we add a % at the end. For whole number percent tiles I rounded to the nearest integer, although can can lead to problems with accuracy. If we need more accuracy you can use .toFixed(n) to save decimal places. For example we could do this to save a single decimal place when needed:

var value = myPieChart.segments[i].value/totalValue*100;
if(Math.round(value) !== value)
    value = (myPieChart.segments[i].value/totalValue*100).toFixed(1);
value = value + '%';

Fiddle Example of percentile with decimals

Fiddle Example of percentile with integers

Anastase answered 27/10, 2015 at 18:53 Comment(9)
@spencer...perfect!!..Thanks ..but here var value = myPieChart.segments[i].value; I wish the value would be in percentage of pie chart..Can you tell me how to do here..Rh
@Rh The value is explicitly defined in the JS Object as a number, and the pie chart looks at those values to determine how much of the chart they take up. So you can get that by doing currentValue/totalValue to get the percentile (ex: =>0.50). Times that by 100 =>50, then add a "%" on the end. Example here.Anastase
If the values are low .0.8% and 1%..If these values are nearby both values are override.it can not be readable.I tried with modifying angle,but totally confusing.How would you do?..I added the screenshot above.Rh
@Rh One way that could help is to change how far the text is drawn on the canvas (determined by (radius/2) in the code) for small values. For small values (say less than 5%), you may want to draw them on the edge or past the edge of the drawn graph, and along with that display force the display to be an integer. I'll give an example tomorrow.Anastase
Wow - very impressive answer; if it works for me, I'm going to Bountify it myself, because I've searched high and low for this answer.Memoried
If you feel like it and have time, please czech out #39646820 (will be bountified for 500 points ASAP)Memoried
This works perfectly, but I didn't got the sin and cos part. Can anybody explain why we had to multiply r/2 with sin of middle angle?Ferule
@Amanjangra Here is a diagram visually showing each item (Made in MS Paint just now). This is is a unit circle with a radius of radius, cos(angle) tells us where to go in the X direction and sin(angle) where to go in the Y direction. The radius/2 is the actual scalar on how far to move in that direction. 0 would be in the center of the circle, r/2 would be in the middle of the circle, and r would be at the very edges of the circle.Anastase
@SpencerWieczorek Thanks, just needed to actully see it. Thanks again.Ferule
T
18

You can make use of PieceLabel plugin for Chart.js.

{ pieceLabel: { mode: 'percentage', precision: 2 } }

Demo | Documentation

The plugin appears to have a new location (and name): Demo Docs.

Trincomalee answered 12/6, 2017 at 9:20 Comment(1)
I want to use mode percentage concate with the label of legend.Triclinic
L
4

For Chart.js 3

I've modified "Hung Tran"'s Code.

animation: {
  onProgress: function() {
    // console.error('this', this);
    const ctx = this.ctx;
    // ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
    ctx.textAlign = 'center';
    ctx.textBaseline = 'bottom';

    let dataSum = 0;
    if(this._sortedMetasets.length > 0 && this._sortedMetasets[0].data.length > 0) {
      const dataset = this._sortedMetasets[0].data[0].$context.dataset;
      dataSum = dataset.data.reduce((p, c) => p + c, 0);
    }
    if(dataSum <= 0) return;

    this._sortedMetasets.forEach(meta => {
      meta.data.forEach(metaData => {
        const dataset = metaData.$context.dataset;
        const datasetIndex = metaData.$context.dataIndex;
        const value = dataset.data[datasetIndex];
        const percent = (Math.round(value / dataSum * 1000) / 10) + '%';
        const mid_radius = metaData.innerRadius + (metaData.outerRadius - metaData.innerRadius) * 0.7;
        const start_angle = metaData.startAngle;
        const end_angle = metaData.endAngle;
        if(start_angle === end_angle) return; // hidden
        const mid_angle = start_angle + (end_angle - start_angle) / 2;

        const x = mid_radius * Math.cos(mid_angle);
        const y = mid_radius * Math.sin(mid_angle);

        ctx.fillStyle = '#fff';
        ctx.fillText(percent, metaData.x + x, metaData.y + y + 15);
      });
    });
  }
}
Lorsung answered 29/3, 2022 at 1:50 Comment(0)
G
3

@Hung Tran's answer works perfect. As an improvement, I would suggest not showing values that are 0. Say you have 5 elements and 2 of them are 0 and rest of them have values, the solution above will show 0 and 0%. It is better to filter that out with a not equal to 0 check!

          var val = dataset.data[i];
          var percent = String(Math.round(val/total*100)) + "%";

          if(val != 0) {
            ctx.fillText(dataset.data[i], model.x + x, model.y + y);
            // Display percent in another line, line break doesn't work for fillText
            ctx.fillText(percent, model.x + x, model.y + y + 15);
          }

Updated code below:

var data = {
    datasets: [{
        data: [
            11,
            16,
            7,
            3,
            14
        ],
        backgroundColor: [
            "#FF6384",
            "#4BC0C0",
            "#FFCE56",
            "#E7E9ED",
            "#36A2EB"
        ],
        label: 'My dataset' // for legend
    }],
    labels: [
        "Red",
        "Green",
        "Yellow",
        "Grey",
        "Blue"
    ]
};

var pieOptions = {
  events: false,
  animation: {
    duration: 500,
    easing: "easeOutQuart",
    onComplete: function () {
      var ctx = this.chart.ctx;
      ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
      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,
              total = dataset._meta[Object.keys(dataset._meta)[0]].total,
              mid_radius = model.innerRadius + (model.outerRadius - model.innerRadius)/2,
              start_angle = model.startAngle,
              end_angle = model.endAngle,
              mid_angle = start_angle + (end_angle - start_angle)/2;

          var x = mid_radius * Math.cos(mid_angle);
          var y = mid_radius * Math.sin(mid_angle);

          ctx.fillStyle = '#fff';
          if (i == 3){ // Darker text color for lighter background
            ctx.fillStyle = '#444';
          }

          var val = dataset.data[i];
          var percent = String(Math.round(val/total*100)) + "%";

          if(val != 0) {
            ctx.fillText(dataset.data[i], model.x + x, model.y + y);
            // Display percent in another line, line break doesn't work for fillText
            ctx.fillText(percent, model.x + x, model.y + y + 15);
          }
        }
      });               
    }
  }
};

var pieChartCanvas = $("#pieChart");
var pieChart = new Chart(pieChartCanvas, {
  type: 'pie', // or doughnut
  data: data,
  options: pieOptions
});
Guaiacum answered 27/12, 2016 at 6:32 Comment(0)
C
0

Give the option for pie chart

onAnimationProgress: drawSegmentValues like:

var pOptions = {
  onAnimationProgress: drawSegmentValues
};
var pieChart = new Chart(pieChartCanvas, {
  type: 'pie', // or doughnut
  data: data,
  options: pOptions
});
Cajole answered 5/4, 2021 at 12:36 Comment(0)
E
-4

Easiest way to do this with Chartjs. Just add below line in options:

pieceLabel: {
        fontColor: '#000'
    }

Best of luck

Endmost answered 24/7, 2018 at 5:55 Comment(3)
Doesn't this require a plugin?Resource
it requires only plugin of chart js. Check this link ... emn178.github.io/Chart.PieceLabel.js/samples/demo ... Hope this will help you to understand.Endmost
This requires PieceLabel, which is a plugin for the ChartJS library and has already been suggested in several answers.Unsteady

© 2022 - 2024 — McMap. All rights reserved.