React+Chart.js Marimekko图表:堆叠柱宽度自定义问题求助
解决React + Chart.js Marimekko图表自定义柱宽与标签显示问题
核心问题分析
Chart.js默认条形图采用等宽类别轴,barPercentage和categoryPercentage是全局配置,无法为单个堆叠柱设置差异化宽度。要实现Marimekko图表的自定义宽度,需改用**线性轴(linear scale)**手动控制每个柱的位置与宽度,同时通过插件实现百分比与数值标签。
实现步骤与代码示例
1. 安装依赖
确保安装所需包:
npm install chart.js react-chartjs-2 chartjs-plugin-datalabels
2. 完整组件代码
import React from 'react'; import { Bar } from 'react-chartjs-2'; import { Chart as ChartJS, LinearScale, BarElement, Title, Tooltip, Legend, Filler } from 'chart.js'; import ChartDataLabels from 'chartjs-plugin-datalabels'; // 注册Chart.js所需模块 ChartJS.register( LinearScale, BarElement, Title, Tooltip, Legend, Filler, ChartDataLabels ); const MarimekkoChart = () => { // 定义类别及对应宽度占比(总宽度为100%) const categories = [ { name: '<7.5 Oz.', width: 40, start: 0 }, { name: '7.5-12.5 Oz.', width: 20, start: 40 }, { name: '12.5-17.5 Oz.', width: 10, start: 60 }, { name: '17.5+ Oz.', width: 30, start: 70 }, ]; // 堆叠系列的原始数据(替换为你的真实数据) const seriesData = [ { label: '产品A', values: [15, 8, 3, 12] }, { label: '产品B', values: [20, 7, 2, 15] }, { label: '产品C', values: [5, 5, 5, 3] }, ]; // 计算每个类别下的总数值(用于百分比计算) const categoryTotals = categories.map((_, idx) => seriesData.reduce((sum, item) => sum + item.values[idx], 0) ); // 转换数据适配线性轴格式 const datasets = seriesData.map((series, seriesIdx) => ({ label: series.label, data: categories.map((cat, catIdx) => ({ x: cat.start + cat.width / 2, // 柱的中心x坐标 y: series.values[catIdx], })), backgroundColor: [`rgba(255, 99, 132, 0.6)`, `rgba(54, 162, 235, 0.6)`, `rgba(255, 206, 86, 0.6)`][seriesIdx], barThickness: () => categories[catIdx]?.width || 0, // 绑定对应类别宽度 datalabels: { // 堆叠内显示百分比 formatter: (value, ctx) => `${Math.round((value.y / categoryTotals[ctx.dataIndex]) * 100)}%`, color: '#fff', anchor: 'center', align: 'center', }, })); // 添加柱顶总数值的数据集(透明背景不影响堆叠) const totalDataset = { label: '总计', data: categories.map((cat, catIdx) => ({ x: cat.start + cat.width / 2, y: categoryTotals[catIdx], })), backgroundColor: 'transparent', borderColor: 'transparent', barThickness: 0, datalabels: { formatter: (value) => value.y, color: '#222', anchor: 'end', align: 'top', offset: 8, font: { weight: 'bold' }, }, }; datasets.push(totalDataset); const chartOptions = { indexAxis: 'y', // 水平条形图(垂直图改为'x') responsive: true, plugins: { legend: { position: 'top' }, title: { display: true, text: 'Marimekko 图表' }, tooltip: { mode: 'index', intersect: false }, }, scales: { x: { type: 'linear', min: 0, max: 100, ticks: { // 自定义x轴刻度标签,对应类别名称 callback: (value) => { const matchCat = categories.find(cat => value >= cat.start && value <= cat.start + cat.width); return matchCat ? matchCat.name : ''; }, autoSkip: false, maxRotation: 0, }, grid: { display: false }, }, y: { stacked: true, // 开启y轴堆叠 grid: { display: true }, }, }, }; return <Bar data={{ datasets }} options={chartOptions} />; }; export default MarimekkoChart;
关键配置说明
自定义柱宽
- 改用线性x轴,将总宽度设为100(对应百分比)
- 每个类别定义
start(起始位置)和width(宽度占比),柱的中心x坐标为start + width/2 - 通过
barThickness绑定对应类别的宽度值,实现差异化占比
标签显示
- 堆叠内百分比:通过
chartjs-plugin-datalabels的formatter计算当前值占类别总值的比例 - 柱顶总数值:新增透明数据集,专门显示每个类别的总和,通过
datalabels设置标签位置与样式
- 堆叠内百分比:通过
轴与堆叠设置
- 开启
y.stacked: true实现堆叠效果 - 自定义x轴刻度标签,匹配每个类别的位置范围
- 开启
内容的提问来源于stack exchange,提问作者alankritha mani
相关产品推荐
相关产品推荐

