[Chartjs]-ChartJS 2.0 – Huddle labels on pie chart

1👍

NOTE: below is not working code. just a layout.

x,y = 0,0 = middle of pie chart / middle of the circle

//these 4 points = part of the donut pie.

xa[1],ya[1] = x,y point on the outside of circle.
xb[1],yb[1] = x,y point on the outside of circle.
xc[1],yc[1] = x,y point on the inside of circle.
xd[1],yd[1] = x,y point on the inside of circle.

//finding middle of above coordinates for outer circle only

xab[1],yab[1] = x,y point on the outside circle, between xa,ya and xb,yb

//you need to find text height in pixels.

function somefunc(){
  //i do know how to do this. 
}

//this is to half height of text so line can be drawn to mid point of text.

var label_height_pixel[1] = //no idea how to figure this out
var find_middle_of_text[1] = label_height_pixel[1] / 2;

//making a larger circle and then adding a little padding so text is not right on circle.

//ok bad code figure out what the x,y coordinates are for this. using

xab[1],yab[1] and x,y
var xtext[1],ytext[1] = outer_circle_radius + find_middle_of_text[1] + padding

run a for loop irritating over over everything above.
with changing the last code above. so that it adds each time it adds a bit more. so no labels will run over top of each other.

my_next_height[2] = outer_circle_radius + label_height_pixel[1] + find_middle_of_text[2] + padding
my_next_height[3] = outer_circle_radius + label_height_pixel[1] + label_height_pixel[2] + find_middle_of_text[3] + padding
my_next_height[4] = outer_circle_radius + label_height_pixel[1] + label_height_pixel[2] + label_height_pixel[3] + find_middle_of_text[3] + padding

you will need to put in a if/then/else function in. to check for x is positive or negative number. and draw line from my_next_height[] either left or right.

then draw in your text.

there is a bit more to drawing the line from outer circle out, then line out so far, then placing the label. but above chopped up layout of code, should give you enough coordinates to make that happen.

i try to avoid x,y and z coordinates. i never can remember the math to find point and angles, without opening up the old grade school math book. and looking at your code mid_radius, and over all code, i am not seeing the normal math that i do remember in finding correct x,y plot points.

0👍

try this one if could help you, as simple solution from charjs

$.plot('#placeholder', data, {
    series: {
        pie: {
            show: true
        }
    },
    legend: {
        show: false
    }
});

0👍

This is a draft answer. Its main purpose is to give you hope. No way is it final (it will be final in a couple of days), because:

  • The code is ugly, hacked, undocumented. It is based on the plugin you provided.
  • The code requires the next minor version of Chart.js 2 (2.3.1 probably). I have built today’s master branch of Chart.js and uploaded it in my Dropbox, because the recently merged layout.padding option was needed, in order to provide room around the pie for the orbiting labels (without this option labels would get cut off).
  • Improvements are definitely needed.

Here is the demo.

A small line will connect each slice of the pie to the corresponding label in orbit. Consecutive labels (apart from the first and the last, if the dataset has an odd number of elements) will orbit at different radii, so that clashes may be avoided. But more care is needed, I guess. The main problem is how to make sure that the chart is responsive. layout.padding refers to pixels, not percentages. Also the label font is currently fixed with respect to size. I think that just setting the position of the labels to a fixed distance from the canvas border (since the padding is fixed as well) would solve that problem (resizing the chart will give some ideas, but more on that later).

The demo is also available below, but it fails to load the JavaScript file from Dropbox (while JSFiddle should not fail – when the CDN version of Chart.js is updated, I will update all links):

var drawItemsValuesPlugin = {
  afterDraw: function(chartInstance) {

    var ctx = chartInstance.chart.ctx;

    // 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.strokeStyle = 'black';
    ctx.font = "bold 11pt Arial";
    ctx.lineWidth = 0.3;

    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,
          total = dataset._meta[Object.keys(dataset._meta)[0]].total,
          mid_radius = model.innerRadius + (2.4 * model.outerRadius - model.innerRadius) / 2,
          start_angle = model.startAngle,
          end_angle = model.endAngle,
          mid_angle = start_angle + (end_angle - start_angle) / 2;

        var mid_radius2 = 0.92 * (model.innerRadius + (2.4 * model.outerRadius - model.innerRadius) / 2);

        var fact = i % 2 == 0 ? 1.00 : 1.15;

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

        var myX = mid_radius2 * fact * Math.cos(mid_angle);
        var myY = mid_radius2 * fact * Math.sin(mid_angle);

        var myX2 = 0.91 * mid_radius2 * 1 * Math.cos(mid_angle);
        var myY2 = 0.91 * mid_radius2 * 1 * Math.sin(mid_angle);

        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.moveTo(model.x + myX2, model.y + myY2);
        ctx.strokeStyle = '#232323';
        ctx.lineTo(model.x + myX, model.y + myY);
        ctx.stroke();
        ctx.strokeStyle = 'black';
        ctx.fillStyle = '#454545';
        ctx.lineWidth = 0.3;

        var label = chartInstance.config.data.labels[i];
        var percent = String(Math.round(dataset.data[i] / total * 100)) + "%";
        ctx.fillText(label, model.x + x, model.y + y);
        ctx.strokeText(label, model.x + x, model.y + y);
        // Display percent in another line, line break doesn't work for fillText
        ctx.fillText(dataset.data[i] + ' (' + percent + ')', model.x + x, model.y + y + 15);
        ctx.strokeText(dataset.data[i] + ' (' + percent + ')', model.x + x, model.y + y + 15);
      }
    });
  }
};

Chart.pluginService.register(drawItemsValuesPlugin);

var ctx = document.getElementById("myChart");

var myChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
      data: [15, 1, 1, 1, 45, 1],
      backgroundColor: [
        'rgba(255, 99, 132, 0.2)',
        'rgba(54, 162, 235, 0.2)',
        'rgba(255, 206, 86, 0.2)',
        'rgba(75, 192, 192, 0.2)',
        'rgba(153, 102, 255, 0.2)',
        'rgba(255, 159, 64, 0.2)'
      ],
      borderColor: [
        'rgba(255,99,132,1)',
        'rgba(54, 162, 235, 1)',
        'rgba(255, 206, 86, 1)',
        'rgba(75, 192, 192, 1)',
        'rgba(153, 102, 255, 1)',
        'rgba(255, 159, 64, 1)'
      ],
      borderWidth: 1
    }]
  },
  options: {
    legend: {
      display: false
    },
    layout: {
      padding: 140
    }
  }
});
<script src="https://dl.dropboxusercontent.com/s/yju3s0dqe32um2k/Chart.min.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>

Leave a comment