Home > Back-end >  How to place text inside of doughnut chart using primeng/chart.js?
How to place text inside of doughnut chart using primeng/chart.js?

Time:09-07

I'm working on a primeng chart and developing a doughnut chart along with angular. Now I need to show the text inside of the doughnut chart as per design . Like the image below

image link

My HTML :

<p-chart
            type="doughnut"
            [data]="inspectionStorage"
            [options]="doughnutOptions"
          ></p-chart>

My Component side code :

this.inspectionStorage = {
              labels: ['Completed', 'Due', 'Pending', 'Deficient', 'Report'],
              datasets: [
                {
                  data: [],
                  backgroundColor: [
                    'green',
                    '#51087E',
                    '#ffd740',
                    'red',
                    '#5500FF',
                  ],
                },
              ],
            };

            this.doughnutOptions = {
              responsive: true,
              plugins: {
                legend: {
                  position: 'right',
                  labels: {
                    boxWidth: 17,
                    boxHeight: 15,
                    color: '#000000',
                  },
                },
              },
            };

But I could not get the exact way I expected the result. So , does anyone advise me on this issue?

CodePudding user response:

There is a specific plugin (but I have never tested) https://github.com/alexkuc/chartjs-plugin-doughnutlabel-rebourne.

Or you could develop your own plugin to do that (snippet is just a starting point to address your use case).

const ctx = document.getElementById("myChart");
let selectedDatasetIndex = 0, selectedIndex = 0;
const getVisibleDataItem = function(chart, metadata) {
  const data = metadata.data;
  for (let i = 0; i < data.length; i  ) {
    if (chart.getDataVisibility(i)) {
        return i;
      }
  }
  return NaN;
}
const showPercentage = function(chart) {
  const ctx = chart.ctx;
  const {width, height, top} = chart.chartArea;
  const centerX = width / 2;
  const centerY = height / 2   top;
  ctx.save();
  ctx.font = 'bolder 32px Arial';
  const metrics = ctx.measureText('100.0%');
  ctx.clearRect(centerX - metrics.width / 2, centerY - 16, metrics.width, 64);
  const metadata = chart.getDatasetMeta(selectedDatasetIndex); 
  if (!chart.getDataVisibility(selectedIndex)) {
      const index = getVisibleDataItem(chart, metadata);
      if (isNaN(index)) {
          return ctx.restore();
      }
      selectedIndex = index;
  }
  const metadataItem = metadata.data[selectedIndex];
  const sum = metadata.total;
  const value = chart.config.data.datasets[selectedDatasetIndex].data[selectedIndex];
  const percentage = value / sum * 100;
  const text = percentage.toFixed(1)   '%';
  ctx.fillStyle = metadataItem.options.backgroundColor;
  ctx.textBaseline = 'bottom';
  ctx.textAlign = 'center';
  ctx.fillText(text, centerX, centerY);
  ctx.textBaseline = 'top';
  ctx.fillText('Completed', centerX, centerY);
  ctx.restore();
};
const pluginShowPercentage = {
  id: 'showPercentage',
  afterDraw: function(chart) {
    if (selectedDatasetIndex >= 0) {
      showPercentage(chart);
    }
  }
}
const myChart = new Chart(ctx, {
    type: 'doughnut',
    plugins: [pluginShowPercentage],
    data: {
      labels: ["Jan", "Feb", "Mar", "Apr", "May"],
        datasets: [{
          data: [120, 23, 24, 45, 154],
          backgroundColor: ['red', 'blue', 'green', 'orange', 'gray']
        }]
    },
    options: {
      responsive: true,
      onClick(e, elements, chart) {
        if (elements.length > 0) {
          const {index, datasetIndex} = elements[0];
          selectedDatasetIndex = datasetIndex;
          selectedIndex = index;
          chart.draw();
        }
      },
      plugins: {
        tooltip: false
      }
   }
});
.myChartDiv {
  max-width: 512px;
  max-height: 340px;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js"></script>
<html>
  <body>
    <div >
      <canvas id="myChart" width="512" height="340"></canvas>
    </div>
  </body>
</html>

.

CodePudding user response:

app.component.ts

import { Component, OnInit } from '@angular/core';
import { Chart } from 'chart.js';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  chart: any;

  ngOnInit() {
    this.chart = new Chart('canvas', {
      type: 'doughnut',
      data: {
        labels: ['Data1', 'Data2'],
        datasets: [
          {
            data: [55, 45],
            backgroundColor: ['rgba(255, 0, 0, 1)', 'rgba(255, 0, 0, 0.1)'],
            fill: false,
          },
        ],
      },
      options: {
        legend: {
          display: false,
        },
        tooltips: {
          enabled: false,
        },
      },
      plugins: [
        {
          id: 'text',
          beforeDraw: function (chart, a, b) {
            var width = chart.width,
              height = chart.height,
              ctx = chart.ctx;

            ctx.restore();
            var fontSize = (height / 114).toFixed(2);
            ctx.font = fontSize   'em sans-serif';
            ctx.textBaseline = 'middle';

            var text = '75%',
              textX = Math.round((width - ctx.measureText(text).width) / 2),
              textY = height / 2;

            ctx.fillText(text, textX, textY);
            ctx.save();
          },
        },
      ],
    });
  }
}

the working example is here

  • Related