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

Chart.js堆叠柱状图问题:如何将方形柱改为圆角柱?

问题描述

我之前用Chart.js做的图表一直正常,现在需要做一个支持x轴、y轴堆叠的3个柱状图,但没法实现预期的圆角堆叠效果。尝试把3个圆角柱拆成两部分,一开始看似可行,但其中的方形柱没法改成圆角。

以下是我的图表配置代码:

ChartJS.defaults.borderColor = 'transparent';
ChartJS.defaults.elements.bar.borderRadius = 50;
ChartJS.defaults.elements.bar.borderSkipped = false;

setChartOptions({
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: {
      position: 'top',
      labels: {
        boxWidth: 10,
        boxHeight: 10,
        useBorderRadius: true,
        borderRadius: 5,
        textAlign: 'right',
      },
    },
    title: {
      display: true,
      text: 'Chart',
    },
    tooltip: {
      enabled: false,
      external: (context) => {
        if (!currentRoot) {
          return;
        }
        currentRoot.render(
          <ChartTooltip
            dict={dict}
            context={context}
            onlyPositive
          />,
        );
      },
    },
  },
  scales: {
    x: {
      type: 'time',
      grid: {
        display: false,
      },
      time: {
        unit: 'hour',
        displayFormats: {
          hour: 'H:mm',
        },
      },
      ticks: {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        callback: (value, index) => {
          switch (selectedRange) {
            case TimeRange.Today:
              if (index % 4 === 0) {
                return DateFNS.format(
                  new Date(value),
                  index === 0 ? 'H:mm' : 'k:mm',
                );
              }
              return '';
            case TimeRange.Week:
              return DateFNS.format(new Date(value), 'E');
            case TimeRange.Month:
              return DateFNS.format(new Date(value), 'd');
            case TimeRange.Year:
              return DateFNS.format(new Date(value), 'LLL');
              return '';
          }
        },
        sampleSize: 1,
        autoSkip: false,
        source: 'labels',
      },
      stacked: true,
      alignToPixels: true,
    },
    y: {
      beginAtZero: true,
      stacked: true,
      grid: {
        drawTicks: false,
      },
      ticks: {
        callback: (value) => {
          if (typeof value === 'string') {
            return value;
          }
          return value < 0 ? -value : value;
        },
      },
      border: {
        display: false,
      },
    },
  },
});
解决方案

Chart.js 默认的全局 borderRadius 设置只会作用在堆叠组的最外层柱子,内层堆叠的柱子不会自动应用圆角。要解决这个问题,可以通过自定义柱子的圆角规则,根据每个柱子在堆叠中的位置动态设置圆角:

  1. 移除全局的 borderRadius 设置,改为在数据集层面通过回调函数控制
  2. 利用 Chart.js 的 beforeUpdate 插件钩子,遍历每个柱子,判断它在堆叠中的位置,为顶部/底部的柱子设置圆角,中间的柱子可按需调整

示例代码修改如下:

// 移除全局的borderRadius设置,改为按需配置
ChartJS.defaults.borderColor = 'transparent';
ChartJS.defaults.elements.bar.borderSkipped = false;

setChartOptions({
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: {
      position: 'top',
      labels: {
        boxWidth: 10,
        boxHeight: 10,
        useBorderRadius: true,
        borderRadius: 5,
        textAlign: 'right',
      },
    },
    title: {
      display: true,
      text: 'Chart',
    },
    tooltip: {
      enabled: false,
      external: (context) => {
        if (!currentRoot) {
          return;
        }
        currentRoot.render(
          <ChartTooltip
            dict={dict}
            context={context}
            onlyPositive
          />,
        );
      },
    },
    // 添加自定义插件处理堆叠柱子的圆角
    beforeUpdate: function(chart) {
      const datasets = chart.data.datasets;
      // 遍历每个数据集
      datasets.forEach((dataset, datasetIndex) => {
        // 遍历每个柱子
        dataset.data.forEach((value, index) => {
          // 获取当前柱子的堆叠组数据
          const stackGroup = chart.scales.y.getStackedValue(index, dataset.stack);
          const totalStack = stackGroup.total;
          const currentValue = Math.abs(value);
          const isTop = stackGroup.end === totalStack; // 当前柱子是堆叠组的顶部
          const isBottom = stackGroup.start === 0; // 当前柱子是堆叠组的底部

          // 为柱子设置圆角:顶部柱子上圆角,底部柱子下圆角,中间柱子按需设置
          dataset.elements = dataset.elements || [];
          dataset.elements[index] = {
            ...dataset.elements[index],
            borderRadius: {
              topLeft: isTop ? 50 : 0,
              topRight: isTop ? 50 : 0,
              bottomLeft: isBottom ? 50 : 0,
              bottomRight: isBottom ? 50 : 0,
            }
          };
        });
      });
    }
  },
  scales: {
    x: {
      type: 'time',
      grid: {
        display: false,
      },
      time: {
        unit: 'hour',
        displayFormats: {
          hour: 'H:mm',
        },
      },
      ticks: {
        callback: (value, index) => {
          switch (selectedRange) {
            case TimeRange.Today:
              if (index % 4 === 0) {
                return DateFNS.format(
                  new Date(value),
                  index === 0 ? 'H:mm' : 'k:mm',
                );
              }
              return '';
            case TimeRange.Week:
              return DateFNS.format(new Date(value), 'E');
            case TimeRange.Month:
              return DateFNS.format(new Date(value), 'd');
            case TimeRange.Year:
              return DateFNS.format(new Date(value), 'LLL');
              return '';
          }
        },
        sampleSize: 1,
        autoSkip: false,
        source: 'labels',
      },
      stacked: true,
      alignToPixels: true,
    },
    y: {
      beginAtZero: true,
      stacked: true,
      grid: {
        drawTicks: false,
      },
      ticks: {
        callback: (value) => {
          if (typeof value === 'string') {
            return value;
          }
          return value < 0 ? -value : value;
        },
      },
      border: {
        display: false,
      },
    },
  },
});

关键说明

  • 通过 beforeUpdate 钩子,在图表更新前动态计算每个柱子的堆叠位置
  • 利用 scales.y.getStackedValue 获取堆叠组的起始和结束值,判断当前柱子是顶部、底部还是中间层
  • 为不同位置的柱子设置对应的圆角,实现堆叠柱状图的全圆角效果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:25:35