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
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();
},
},
],
});
}
}