0

Hello everyone I am a newbie in using chart.js package, is there a way I can create is king of chart?enter image description here

pinkywest
  • 25
  • 8
  • what **data** should be displayed? what do the arrows mean? should the chart be static? Have you tried something? is the answer "Yes, there is" or "No, there is no way" enough? please provide more details. Thank you :) – winner_joiner Dec 07 '22 at 10:30
  • did you checkout this question & anwser https://stackoverflow.com/a/57992288/1679286 – winner_joiner Dec 07 '22 at 10:32
  • @winner_joiner the arrow point to the percentage value, the rest in the pie chart is a static data value for bad (red part 0-20%), Okay (yellow part 21 - 70%) and Good (green part 71 - 100%) – pinkywest Dec 08 '22 at 06:54

1 Answers1

1

Yes, it is possible.

  1. There might be already a plugin, that can achieve this (but I don't know any, but I didn't really look), so you could look for one,
  2. Or you could simply write your own plugin, with a few lines of code

Here a demo, how I would do this, with an inline plugin:
(three charts with 3 different percentages)

var arrowPlugin = {
    id: 'arrowPlugin',
    afterDraw: (chart, args, options) => {
        const { ctx } = chart;
        
        let midX = chart.width / 2;
        let midY = chart.height;
        let arrowLength = chart.height / 2;
        
        let text = `${options.value * 100}%`;
        let textSize = ctx.measureText(text);
        
        ctx.save();
        ctx.save();
        
        ctx.translate(midX, midY);
        ctx.rotate( (Math.PI * options.value)  - Math.PI /2);
        
        ctx.strokeStyle = options.arrowColor;
        ctx.fillStyle = options.arrowColor;
        ctx.lineCap = 'butt';
        
        ctx.lineWidth = 3;
        
        ctx.beginPath();
        ctx.moveTo(0, - arrowLength * 3 / 4 + 3)
        ctx.lineTo(0, - arrowLength * 6 / 4);
        ctx.stroke();
        
        ctx.beginPath();
        ctx.moveTo(0, - arrowLength * 6 / 4 - 4)
        ctx.lineTo(5, - arrowLength * 6 / 4 + 10)
        ctx.lineTo(-5, - arrowLength * 6 / 4  + 10)
        ctx.closePath();
        ctx.fill()
        
        
        ctx.fillStyle = options.triangleColor;
        ctx.beginPath();
        ctx.moveTo(0,   -midY + 25)
        ctx.lineTo(-10, -midY + 10)
        ctx.lineTo(10,  -midY + 10)
        ctx.closePath()
        ctx.fill();
        
        
        ctx.restore();
        

        ctx.fillStyle = options.textColor;
        ctx.font = "50px Arial";
        ctx.fillText(text, midX - textSize.width * 1.5, midY - 25);
        
        ctx.restore();
    },
    defaults: {
        // set here the percentage
        value:.50,
        arrowColor: 'black',
        textColor: 'black',
        triangleColor: 'black'
    }
}

var options1 = {
    type: 'doughnut',
    data: {
    datasets: [{
        data: [20, 50, 30],
        backgroundColor: [
            'rgba(231, 76, 60, 1)',
            'rgba(255, 164, 46, 1)',
            'rgba(46, 204, 113, 1)'
        ],
        borderColor: [
            'rgba(255, 255, 255 ,1)',
            'rgba(255, 255, 255 ,1)',
            'rgba(255, 255, 255 ,1)'
        ],
        borderWidth: 0
    }]},
    options: {
      cutout: 33,
      rotation: -90,
      circumference: 180,
      
      maintainAspectRatio: false,
      legend: { display: false },
      
      plugins:{ 
        tooltip: {  enabled: false },
      }
    }, 
    plugins:[arrowPlugin]
}

var options2 = {
    type: 'doughnut',
    data: {
    datasets: [{
        data: [20, 50, 30],
        backgroundColor: [
            'rgba(231, 76, 60, 1)',
            'rgba(255, 164, 46, 1)',
            'rgba(46, 204, 113, 1)'
        ],
        borderColor: [
            'rgba(255, 255, 255 ,1)',
            'rgba(255, 255, 255 ,1)',
            'rgba(255, 255, 255 ,1)'
        ],
        borderWidth: 0
    }]},
    options: {
      cutout: 33,
      rotation: -90,
      circumference: 180,
      
      maintainAspectRatio: false,
      legend: { display: false },
      
      plugins:{ 
        tooltip: {  enabled: false },
        // override default percentage
        arrowPlugin: { value: .20 }
      }
    }, 
    plugins:[arrowPlugin]
}

var options3 = {
    type: 'doughnut',
    data: {
    datasets: [{
        data: [20, 50, 30],
        backgroundColor: [
            'rgba(231, 76, 60, 1)',
            'rgba(255, 164, 46, 1)',
            'rgba(46, 204, 113, 1)'
        ],
        borderColor: [
            'rgba(255, 255, 255 ,1)',
            'rgba(255, 255, 255 ,1)',
            'rgba(255, 255, 255 ,1)'
        ],
        borderWidth: 0
    }]},
    options: {
      cutout: 33,
      rotation: -90,
      circumference: 180,
      
      maintainAspectRatio: false,
      legend: { display: false },
      
      plugins:{ 
        tooltip: {  enabled: false },
        // override default percentage
        arrowPlugin: { value: .70 }
      }
    }, 
    plugins:[arrowPlugin]
}

const chart1 = document.getElementById('chart1')
new Chart(chart1, options1);

const chart2 = document.getElementById('chart2')
new Chart(chart2, options2);

const chart3 = document.getElementById('chart3')
new Chart(chart3, options3);
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div style="width:500px;height:184px">
   <canvas id="chart1" width="500" height="184"></canvas>
   <canvas id="chart2" width="500" height="184"></canvas>
   <canvas id="chart3" width="500" height="184"></canvas>
<div>

the percentage is currently set in the inline plugin-options, default value is set to .5 = 50% (valid range 0 - 1 = 0% - 100%).

winner_joiner
  • 12,173
  • 4
  • 36
  • 61