我正在构建一个生成速度计的 React 组件,我想设置每个段的长度(即红色 - 30%、黄色 - 30%、绿色 - 30% 和灰色 - 10%)。
我正在使用 React-ChartJS-2 和动画的 onComplete 我正在绘制文本和针。
我检查了文档,没有设置每个段的长度或宽度。在理想情况下,针应该是绿色的……是的,我们的数字允许 100+%
任何人都知道如何做到这一点,无论是插件还是回调函数,我都可以绑定到它允许我自定义每个段的绘制方式?
<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);
}
};