You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中react-chartjs-2堆叠柱状图圆角缝隙消除方案咨询

问题:堆叠柱状图中间层圆角缝隙填充

我使用react-chartjs-2实现了带多组堆叠柱子的柱状图,希望所有(包括中间层)柱子的顶部都设置圆角。虽然实现圆角相对容易,但中间层柱子会留下难看的白色缝隙,我希望用下一层柱子的颜色填充这些缝隙。请问这是否可行?我未找到相关配置选项(仅找到backgroundColor和borderColor)。

示例图

实现代码

import React from 'react';
import { Bar } from 'react-chartjs-2';


export const barOptions = {
  responsive: true,
  scales: {
    x: {
      stacked: true,
    },
    y: {
      stacked: true,
    },
  },
  plugins: {
    legend: {
      position: 'top' as const,
    },
  },
  maintainAspectRatio: false,
};

const MyBarExample = () => {

  const [barLabels, setBarLabels] = React.useState<string[]>([]);
  const [dataSet1, setdataSet1] = React.useState<(getDataSet1 | null)[] | null>(null);
  const [dataSet2, setdataSet2] = React.useState<(getDataSet2 | null)[] | null>(null);

  // 获取数据逻辑
  // ...
  

  const barData = {
    labels: barLabels,
    datasets: [
      {
        label: 'Type 1 A',
        data: dataSet1?.map((lng) => lng?.A || '') || [],
        backgroundColor: '#7482FF',
        borderColor: '#7482FF',
        stack: 'Stack 0',
        borderRadius: {
          topLeft: 12,
          topRight: 12,
        },
      },
      {
        label: 'Type 1 B',
        data: dataSet1?.map((lng) => lng?.B || '') || [],
        backgroundColor: 'rgba(116,130,255,0.7)',
        borderColor: 'rgba(116,130,255,0.7)',
        stack: 'Stack 0',
        borderRadius: {
          topLeft: 12,
          topRight: 12,
        },
      },
      {
        label: 'Type 1 C',
        data: dataSet1?.map((lng) => lng?.C || '') || [],
        backgroundColor: 'rgba(116,130,255,0.15)',
        borderColor: 'rgba(116,130,255,0.15)',
        stack: 'Stack 0',
        borderRadius: {
          topLeft: 12,
          topRight: 12,
        },
      },
      {
        label: 'Type 2 A',
        data: dataSet2?.map((lng) => lng?.A || '') || [],
        backgroundColor: '#FF7D45',
        borderColor: '#FF7D45',
        stack: 'Stack 1',
        borderRadius: {
          topLeft: 12,
          topRight: 12,
        },

      },
      {
        label: 'Type 2 B',
        data: dataSet2?.map((lng) => lng?.B || '') || [],
        backgroundColor: 'rgba(255,125,69,0.7)',
        borderColor: 'rgba(255,125,69,0.7)',
        stack: 'Stack 1',
        borderRadius: {
          topLeft: 12,
          topRight: 12,
        },
      },
      {
        label: 'Type 2 C',
        data: dataSet2?.map((lng) => lng?.C || '') || [],
        backgroundColor: 'rgba(255,125,69,0.15)',
        borderColor: 'rgba(255,125,69,0.15)',
        stack: 'Stack 1',
        borderRadius: {
          topLeft: 12,
          topRight: 12,
        },
      },
    ],
  };

  return (
    <Bar width="100%" height="100%" options={barOptions} data={barData} />
  );
};

export default MyBarExample;

解决方案

这是可行的,Chart.js本身没有直接配置项解决该问题,但可以通过自定义插件实现无缝填充:

步骤1:编写自定义填充插件

该插件会在图表绘制前,遍历每个堆叠组的柱子,用下层柱子的颜色填充上层圆角留下的缝隙:

const fillStackedBarGapPlugin = {
  id: 'fillStackedBarGap',
  beforeDraw(chart) {
    const ctx = chart.ctx;
    const datasets = chart.data.datasets;
    if (datasets.length === 0) return;

    // 按堆叠分组
    const stackGroups = {};
    datasets.forEach((dataset, idx) => {
      const stackKey = dataset.stack || 'default';
      if (!stackGroups[stackKey]) stackGroups[stackKey] = [];
      stackGroups[stackKey].push({
        dataset,
        meta: chart.getDatasetMeta(idx)
      });
    });

    // 处理每个堆叠组
    Object.values(stackGroups).forEach(group => {
      // 遍历每个标签对应的柱子
      group[0].meta.data.forEach((_, labelIndex) => {
        let prevBarTop = group[group.length - 1].meta.data[labelIndex].base; // 初始为最底层柱子的底部

        // 从下往上遍历柱子(从底层到顶层)
        for (let i = group.length - 1; i >= 0; i--) {
          const currentBar = group[i].meta.data[labelIndex];
          const currentDataset = group[i].dataset;
          const borderRadius = currentDataset.borderRadius || {};
          const radius = Math.min(
            borderRadius.topLeft || 0,
            borderRadius.topRight || 0,
            currentBar.width / 2
          );

          if (radius > 0 && i < group.length - 1) {
            // 用下层柱子的颜色填充缝隙
            const fillColor = group[i + 1].dataset.backgroundColor;
            ctx.save();
            ctx.fillStyle = typeof fillColor === 'string' ? fillColor : fillColor[labelIndex];

            // 绘制左上角圆角缝隙填充
            ctx.beginPath();
            ctx.arc(
              currentBar.x - currentBar.width / 2 + radius,
              currentBar.y + radius,
              radius,
              Math.PI,
              Math.PI * 1.5
            );
            ctx.fill();

            // 绘制右上角圆角缝隙填充
            ctx.beginPath();
            ctx.arc(
              currentBar.x + currentBar.width / 2 - radius,
              currentBar.y + radius,
              radius,
              Math.PI * 1.5,
              Math.PI * 2
            );
            ctx.fill();
            ctx.restore();
          }

          prevBarTop = currentBar.y;
        }
      });
    });
  }
};

步骤2:在组件中注册插件

修改barOptions,将自定义插件加入配置:

export const barOptions = {
  responsive: true,
  scales: {
    x: { stacked: true },
    y: { stacked: true },
  },
  plugins: {
    legend: { position: 'top' as const },
  },
  maintainAspectRatio: false,
  // 注册自定义插件
  plugins: [fillStackedBarGapPlugin]
};

注意事项

  • 确保堆叠的柱子在datasets数组中是从底层到顶层排列(比如你的代码中Type 1 C在最底层,Type 1 A在最上层,这个顺序是正确的),否则插件无法正确获取下层柱子的颜色。
  • 如果你的backgroundColor是数组(每个柱子颜色不同),插件会自动对应取同标签下的下层颜色。

内容的提问来源于stack exchange,提问作者Yellow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 12:57:38