2

I'm building a React component that generates a speedometer and I want to set the length of each segment (i.e. Red - 30%, Yellow - 30%, Green - 30%, and Gray - 10%).

I'm using React-ChartJS-2 and onComplete of the animation I'm drawing the text and needle.

I've checked the documentation and there's nothing for setting the length or width of each segment. In an ideal situation, the needle would be in the green... and yes, our numbers allow for 100+%

Anyone have an idea on how to do this, either a plug-in or a callback function that I can tie into that allows me to customize how each segment is drawn?

enter image description here

    <Box>
      <Box style={{ position: 'relative', paddingLeft: theme.spacing(1.25), height: 300 }}>
        <Doughnut
          ref={chartRef}
          data={{
            labels: labels ?? data,
            datasets: [
              {
                data: data,
                // Use backgroundColor if available or generate colors based on number of data points
                backgroundColor: backgroundColor ?? generateColorArray(20, 360, data.length),
                borderWidth: 0
              }
            ]
          }}
          options={{
            legend: {
              display: false,
              position: 'top',
              fullWidth: true
            },
            layout: {
              padding: {
                top: 50,
                left: 25,
                right: 25,
                bottom: 25
              }
            },
            rotation: 1 * Math.PI,
            circumference: Math.PI,
            cutoutPercentage: 70,
            animation: {
              duration: 500,
              easing: 'easeOutQuart',
              onComplete: function(e): void {
                drawNeedle(e.chart.innerRadius, e.chart.outerRadius);
                drawText(`${needleValue.toString()}%`, e.chart.innerRadius, e.chart.outerRadius);
              },
              onProgress: function(e): void {
                console.log('e: ', e);
              }
            },
            tooltips: {
              enabled: false
            },
            // Disable other events from firing which causes the chart elements to get pushed down onHover
            events: []
          }}
        />
      </Box>
    </Box>
const drawNeedle = (innerRadius: number, outerRadius: number): void => {
    const chart = chartRef.current as Doughnut;
    const ctx = chart.chartInstance.ctx;
    const maxValue = 180;

    if (ctx) {
      const rotation = -Math.PI;
      const circumference = Math.PI;
      const origin = rotation + circumference * (0 / maxValue);
      const target = rotation + circumference * (((needleValue / 100) * 180) / maxValue);
      const angle = origin + (target - origin) * 1;
      const chartArea = chart.chartInstance.chartArea;
      const width = chartArea.right - chartArea.left;
      const needleRadius = (2 / 100) * width;
      const needleWidth = (3.2 / 100) * width;
      const needleLength = (20 / 100) * (outerRadius - innerRadius) + innerRadius;

      const cw = ctx.canvas.offsetWidth;
      const ch = ctx.canvas.offsetHeight;
      const cx = cw / 2;
      const cy = ch - ch / 14;

      ctx.save();
      ctx.translate(cx, cy);
      ctx.rotate(angle);
      ctx.fillStyle = 'rgba(0, 0, 0, 1)';

      // draw circle
      ctx.beginPath();
      ctx.ellipse(0, 0, needleRadius, needleRadius, 0, 0, 2 * Math.PI);
      ctx.fill();

      // draw needle
      ctx.beginPath();
      ctx.moveTo(0, needleWidth / 2);
      ctx.lineTo(needleLength, 0);
      ctx.lineTo(0, -needleWidth / 2);
      ctx.fill();

      ctx.restore();
    }
  };

  const drawText = (text: string, innerRadius: number, outerRadius: number): void => {
    const chart = chartRef.current as Doughnut;
    const ctx = chart.chartInstance.ctx;
    const minValue = Math.min(...data);
    const maxValue = Math.max(...data);

    if (ctx) {
      ctx.fillStyle = 'rgba(0, 0, 0, 1)';

      const chartArea = chart.chartInstance.chartArea;
      const centerX = (chartArea.left + chartArea.right) / 2;
      const centerY = (chartArea.top + chartArea.bottom) / 2;
      const textMetrics = ctx.measureText(text);
      const textHeight = Math.max(ctx.measureText('m').width, ctx.measureText('\uFF37').width);

      const radialDiff = outerRadius - innerRadius;

      // Min / Max values
      ctx.font = '20px Arial';
      ctx.fillText(`${minValue}%`, chartArea.left + radialDiff * 1.1, chartArea.bottom + textHeight * 2);
      ctx.fillText(`${maxValue}%`, chartArea.right - radialDiff * 2, chartArea.bottom + textHeight * 2);

      // Needle value
      ctx.font = '30px Arial';
      ctx.fillText(text, centerX - textMetrics.width, centerY + textHeight);
    }
  };

0 Answers0