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 设置只会作用在堆叠组的最外层柱子,内层堆叠的柱子不会自动应用圆角。要解决这个问题,可以通过自定义柱子的圆角规则,根据每个柱子在堆叠中的位置动态设置圆角:
- 移除全局的
borderRadius设置,改为在数据集层面通过回调函数控制 - 利用 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
相关产品推荐
相关产品推荐

