Search code examples
javascriptvue.jschart.jsvue-chartjs

How to show doughnut chart empty with custom color when empty or no data with doughnut rings?


I use this doughnut chart from chartjs in vue using vue-chartjs.

By default the doughnut chart when there are no data or all values are empty it show nothing. Is there a way to show doughnut chart with custom color when there are no values or all the values are 0 in the dataset in the doughnut rings.

Desired behaviour: A doughnut chart with custom color when no data. Like here in this link doughnut-empty-state they only provided with a circle. Instead want full doughnut with custom color input when empty. How Should I do that?

Implementation:

<template>
  <Doughnut
    id="my-chart-id"
    :chartOptions="getChartOptions"
    :chartData="getChartData"
    :plugins="[defaultBackground]"
  />
</template>
<script>
import { Doughnut } from 'vue-chartjs';
import { Chart as ChartJS, ArcElement } from 'chart.js';

ChartJS.register(ArcElement);

export default {
  name: 'DoughnutChart',
  components: { Doughnut },
  props: ['chartData', 'options', 'theme', 'mspDomain', 'rootDomain'],
  data() {
    return {
      defaultBackground: {
        id: 'custom_canvas_background_color',
        afterDraw: function(chart) {
          const {datasets} = chart.data;
          const {color, width, radiusDecrease} = options;
          let hasData = false;

          for (let i = 0; i < datasets.length; i += 1) {
            const dataset = datasets[i];
            hasData |= dataset.data.length > 0;
           }

           if (!hasData) {
              const {chartArea: {left, top, right, bottom}, ctx} = chart;
              const centerX = (left + right) / 2;
              const centerY = (top + bottom) / 2;
              const r = Math.min(right - left, bottom - top) / 2;

              ctx.beginPath();
              ctx.lineWidth = width || 2;
              ctx.strokeStyle = color || 'rgba(255, 128, 0, 0.5)';
              ctx.arc(centerX, centerY, (r - radiusDecrease || 0), 0, 2 * Math.PI);
              ctx.stroke();
           }
        }
      }
    };
  },
  computed: {
    getChartData() {
      return this.chartData;
    },
    getChartOptions() {
      return this.options;
    }
  }
};
</script>

<style></style>

Here it just paints a circle instead of whole doughnut.


Solution

  • I created the doughnut chart in afterDraw plugin with all the calculation and radius as per how I wanted. I modified the code from this reference given in chartjs for empty data.

    Here you can change radius, color according to your requirements.

    <template>
      <Doughnut
        id="my-chart-id"
        :chartOptions="getChartOptions"
        :chartData="getChartData"
        :plugins="[defaultBackground]"
      />
    </template>
    <script>
    import { Doughnut } from 'vue-chartjs';
    import { Chart as ChartJS, ArcElement } from 'chart.js';
    
    ChartJS.register(ArcElement);
    
    export default {
      name: 'DoughnutChart',
      components: { Doughnut },
      props: ['chartData', 'options'],
      data() {
        return {
          defaultBackground: {
            id: 'custom_canvas_background_color',
            afterDraw: function(chart) {
              let totalData = chart.config.data.datasets[0].data.reduce(function(
                a,
                b
              ) {
                return a + b;
              },
              0);
              if (totalData === 0) {
                const {
                  chartArea: { left, top, right, bottom },
                  ctx
                } = chart;
                ctx.save(); // Save the current canvas state
    
                // Calculate the center of the chart
                const centerX = (left + right) / 2;
                const centerY = (top + bottom) / 2;
    
                // Calculate the radii of the inner and outer circles of the doughnut
                const outerRadius = Math.min(right - left, bottom - top) / 2;
                const innerRadius = outerRadius - 45; // Adjust this value as needed
    
                // Calculate the positions for the starting and ending points of the line
                const lineStartX = centerX;
                const lineStartY = centerY - outerRadius;
                const lineEndX = centerX;
                const lineEndY = centerY - innerRadius;
    
                // Draw the outer arc (grey doughnut ring)
                ctx.beginPath();
                ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI);
                ctx.fillStyle = 'rgba(216, 216, 216, 1)';
                ctx.fill();
    
                // Draw the inner arc (to clear the inner circle)
                ctx.beginPath();
                ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI);
                ctx.fillStyle = 'rgba(255, 255, 255, 1)'; // Fill with white to clear the inner circle
                ctx.fill();
    
                // Draw the white line break from outer circle to inner circle
                ctx.beginPath();
                ctx.moveTo(lineStartX, lineStartY);
                ctx.lineTo(lineEndX, lineEndY);
                ctx.lineWidth = 2; // Adjust the line width as needed
                ctx.strokeStyle = 'rgba(255, 255, 255, 1)'; // White color
                ctx.stroke();
    
                ctx.restore(); // Restore the canvas state
              }
            }
          }
        };
      },
      computed: {
        getChartData() {
          return this.chartData;
        },
        getChartOptions() {
          return this.options;
        }
      }
    };
    </script>
    
    <style></style>