如何在 Chart.js 饼图中心动态显示数值文本

本文详解如何在 react 中使用 chart.js + react-chartjs-2 在环形图(doughnut)中心精准绘制动态数值文本,解决因插件注册位置错误导致的文本不显示问题,并提供可直接运行的完整实现方案。

在 Chart.js(v3+)中为 Doughnut 图表添加中心文本,不能将自定义绘图逻辑直接写在 options.plugins 内联对象中——这是原代码失效的根本原因。Chart.js v3 的插件系统要求:插件必须作为独立对象定义,并通过 组件的 plugins prop 显式传入,而非嵌套在 options.plugins 里(后者仅用于配置内置插件,如 tooltip、legend)。

以下是修正后的关键步骤与完整实践代码:

✅ 正确做法:分离插件定义 + 正确传入

// 1. 定义独立插件(注意:必须有唯一 id)
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 28px Inter, sans-serif'; // 推荐使用系统字体栈
    ctx.fillStyle = '#E53E3E';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(`${Math.round(chart.data.datasets[0].data[0])}%`, centerX, centerY);
    ctx.restore();
  }
};

// 2. options 中 plugins 保持为空对象(或仅含配置项)
const doughnutOptions = {
  responsive: true,
  cutout: '75%',
  layout: { padding: 16 },
  plugins: {
    // ⚠️ 这里只放配置(如 tooltip.enabled = false),不放函数!
  }
};

✅ 在组件中正确使用

? 常见陷阱与注意事项

  • chart.ctx 安全性:确保在 beforeDraw 钩子中操作 ctx,此时 canvas 上下文已就绪;避免在 afterDraw 或其他钩子中误用。
  • 动态数据同步:文本内容(如 ${input1})需随 data 实时更新。本例中 input1 是响应式 state,插件内直接读取 chart.data.datasets[0].data[0] 更健壮(自动跟随数据变化)。
  • 字体与抗锯齿:添加 ctx.lineJoin = 'round' 和 ctx.imageSmoothingQuality = 'high' 可提升文本渲染质量。
  • 多图表复用:若多个 Doughnut 需不同中心文本,可将插件封装为工厂函数:
    const createCenterTextPlugin = (getText: (chart: Chart) => string) => ({
      id: 'dynamicCenterText',
      beforeDraw: (chart: Chart) => {
        const ctx = chart.ctx;
        const { left, top, right, bottom } = chart.chartArea;
        ctx.save();
        ctx.font = 'bold 24px sans-serif';
        ctx.fillStyle = '#2D3748';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(getText(chart), (left + right) / 2, (top + bottom) / 2);
        ctx.restore();
      }
    });
    // 使用:plugins={[createCenterTextPlugin((c) => `${c.data.datasets[0].data[0]}%`)]}

✅ 最终效果保障

  • 文本始终居中(利用 textAlign='center' + textBaseline='middle' + 准确计算中心坐标);
  • 动态响应数据变化(state 更新 → re-render → 插件重新执行);
  • 不干扰其他图表功能(tooltip、动画、响应式等均正常)。

通过将插件逻辑解耦并严格遵循 Chart.js v3 插件注入规范,即可稳定、高效地在环形图中心渲染任意文本——这是生产环境推荐的标准实践。