
本文详解如何在 chart.js 的 doughnut 图表中心精准渲染动态文本,重点纠正插件注册位置错误、坐标计算逻辑缺陷及 react 状态同步问题,并提供可直接运行的完整解决方案。
在使用 react-chartjs-2 实现甜甜圈图(Doughnut)时,许多开发者尝试通过 options.plugins.beforedraw 直接注入绘图逻辑来居中显示数值,但常遇到文本不显示、偏移或闪烁等问题。根本原因在于:Chart.js 插件必须作为独立对象传入组件的 plugins prop,而非嵌套在 options.plugins 内部——后者仅用于配置已注册插件,无法定义新插件逻辑。
✅ 正确做法:分离插件定义与配置
首先,将自定义插件提取为独立对象,并赋予唯一 id(这是 Chart.js 插件系统的强制要求):
const centerTextPlugin = { id: 'centerText', beforeDraw: (chart) => { const { ctx, chartArea: { left, top, right, bottom } } = chart; const centerX = left + (right - left) / 2; const centerY = top + (bottom - top) / 2; ctx.save(); ctx.font = 'bold 24px sans-serif'; ctx.fillStyle = '#333'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(`${chart.data.datasets[0].data[0]}`, centerX, centerY); ctx.restore(); } };
⚠️ 注意:chart.ctx 和 chart.chartArea 在 beforeDraw 阶段已完全就绪,但需确保 chart.data 已同步更新(见下文状态处理)。
✅ 配置 options 与 plugins 分离
options 中不应包含插件实现,仅保留图表行为配置;插件需通过
const options = { responsive: true, cutout: '75%', // 建议用百分比而非字符串('80%' → 80) layout: { padding: 16 }, plugins: { legend: { display: false }, // 可选:隐藏图例以突出中心文本 tooltip: { enabled: false } // 可选:禁用悬停提示 } }; // 使用时:
✅ 处理 React 状态与动画同步问题
原代码中 useEffect 的依赖项 [count, targetValue] 会导致无限循环(count 更新触发 effect,effect 又更新 count)。应改为:
useEffect(() => { const durations = [5000, 5000, 5000]; const increment = targetValue.map((val, i) => Math.ceil(val / durations[i] * 10)); const timer = setTimeout(() => { setCount(prev => prev.map((c, i) => (c < targetValue[i] ? c + increment[i] : c)) ); }, 10); return () => clearTimeout(timer); }, [targetValue]); // ✅ 仅依赖 targetValue,避免循环
同时,确保 data 动态绑定最新值(如 data.datasets[0].data[0] 对应 input1),插件内 fillText 才能实时反映变化。
✅ 完整可运行示例(精简版)
import { Doughnut } from 'react-chartjs-2'; import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'; ChartJS.register(ArcElement, Tooltip, Legend); const centerTextPlugin = { /* 如上定义 */ }; export default function RoundedStats() { const [input1, setInput1] = useState(0); // ... 其他 state 同理 useEffect(() => { // 初始化逻辑(略) }, []); const data = { datasets: [{ data: [input1, 100 - input1], backgroundColor: ['red', '#071952'] }] }; const options = { /* 如上配置 */ }; return ( No of Visitors
{/* 其他两个图表同理 */} ); }
? 常见错误排查清单
- ❌ 错误:plugins 写在 options.plugins 里 → ✅ 应传给组件 plugins prop
- ❌ 错误:未设置 id 字段 → ✅ 插件必须有唯一 id
- ❌ 错误:chartArea 计算使用 chart.chartArea.left 等 → ✅ 应解构 chart.chartArea 或用 chart.boxes(更健壮)
- ❌ 错误:input1 未及时同步到 data → ✅ 确保 data 是响应式引用最新 state
通过以上调整,文本将稳定、居中、随数据实时更新地渲染在甜甜圈图正中央。