Chartjs-Chart.js Picture inside doughnut segment

1👍

There is no native ChartJS API for drawing an image inside a donut chart.

But you can manually add the images after the chart has been drawn.

For each wedge in the donut:

Warning: untested code … some tweaking might be required

  1. Translate inward to the middle of the donut.

    // calculate donut center (cx,cy) & translate to it
    var cx=chart.width/2;
    var cy=chart.height/2;
    context.translate(cx,cy);
    
  2. Rotate to the mid-angle of the target donut-wedge

    var startAngle = chart.segments[thisWedgeIndex].startAngle;
    var endAngle = chart.segments[thisWedgeIndex].endAngle;
    var midAngle = startAngle+(endAngle-startAngle)/2;
    
    // rotate by the midAngle
    context.rotate(midAngle);
    
  3. Translate outward to the midpoint of the target donut-wedge:

    // given the donut radius (innerRadius) and donut radius (radius)
    var midWedgeRadius=chart.innerRadius+(chart.radius-chart.innerRadius)/2;
    context.translate(midWedgeRadius,0);
    
  4. Draw the image offset by half the image width & height:

    // given the image width & height
    context.drawImage(theImage,-theImage.width/2,-theImage.height/2);
    
  5. Clean up the transformations by resetting the transform matrix to default:

    // undo translate & rotate
    context.setTransform(1,0,0,1,0,0);
    

1👍

In the new version use the following example, (it requires chartjs-plugin-labels):

import React from 'react';
import { Doughnut } from 'react-chartjs-2';
import 'chartjs-plugin-labels';

const imageURLs = [
  'https://avatars.githubusercontent.com/u/43679262?v=4',
  'https://avatars.githubusercontent.com/u/43679262?v=4',
  'https://avatars.githubusercontent.com/u/43679262?v=4',
];
const images = imageURLs.map((v) => {
  var image = new Image();
  image.src = v;
  return image;
});


export const data_doughnut = {
  labels: ['a', 'b', 'c'],
  datasets: [
    {
      data: [30, 15, 10],
      backgroundColor: [
        '#B1A9FF',
        '#877CF8',
        '#6456F2',
      ],
      weight: 1,
    },
  ],
};

export const chartOptions = {
  responsive: true,
  plugins: {
    legend: {
      display: false,
    },
  },
  scales: {
    ticks: {
      display: false,
    },
  },
};

export const plugins = [
  {
    afterDatasetsDraw: (chart) => {
      var ctx = chart.ctx;
      ctx.save();
      var xCenter = chart.canvas.width / 2;
      var yCenter = chart.canvas.height / 2;
      var data = chart.config.data.datasets[0].data;
      var vTotal = data.reduce((a, b) => a + b, 0);
      data.forEach((v, i) => {
        var vAngle =
          data.slice(0, i).reduce((a, b) => a + b, 0) + v / 2;
        var angle = (360 / vTotal) * vAngle - 90;
        var radians = angle * (Math.PI / 180);
        var r = yCenter;
        // modify position
        var x = xCenter + (Math.cos(radians) * r) / 1.4;
        var y = yCenter + (Math.sin(radians) * r) / 1.4;
        ctx.translate(x, y);
        var image = images[i];
        ctx.drawImage(image, -image.width / 2, -image.height / 2);
        ctx.translate(-x, -y);
      });
      ctx.restore();
    },
  },
];

export function DoughnutChartFeekers() {
  return (
    <Doughnut
      data={data_doughnut}
      plugins={plugins}
      options={chartOptions}
    />
  );
}

Leave a comment