React+Chart.js双X轴堆叠柱状图配置异常求助
双X轴堆叠柱状图实现问题求助
我无法排查当前代码问题,需要实现双X轴堆叠柱状图(参考示例图样式),但当前生成的图表显示异常。以下是接收的JSON数据、初始代码,寻求技术帮助。
接收的JSON数据
[ { "label": "Others", "type": "bar", "stack": "Person 1", "data": [22,0,80] }, { "label": "AWS", "type": "bar", "stack": "Person 1", "data": [2,0,21] }, { "label": "Github Enterprise Cloud - Organization", "type": "bar", "stack": "Person 1", "data": [1,0,8] }, { "label": "Okta Admin Console", "type": "bar", "stack": "Person 1", "data": [2,0,10] }, { "label": "PacificTimeSheet", "type": "bar", "stack": "Person 1", "data": [0,0,12] }, { "label": "ArgoCD - Dev", "type": "bar", "stack": "Person 1", "data": [0,0,0] }, { "label": "kubero", "type": "bar", "stack": "Person 1", "data": [0,0,0] }, { "label": "Okta Dashboard", "type": "bar", "stack": "Person 1", "data": [0,0,0] }, { "label": "ArgoCD - Production IPV6", "type": "bar", "stack": "Person 1", "data": [0,0,0] }, { "label": "Okta Browser Plugin", "type": "bar", "stack": "Person 1", "data": [0,0,0] }, { "label": "Vercel", "type": "bar", "stack": "Person 1", "data": [0,0,0] }, { "label": "ONEDigital Portal Prod", "type": "bar", "stack": "Person 1", "data": [0,0,6] }, { "label": "Others", "type": "bar", "stack": "Person 2", "data": [8,0,16] }, { "label": "AWS", "type": "bar", "stack": "Person 2", "data": [2,0,1] }, { "label": "Github Enterprise Cloud - Organization", "type": "bar", "stack": "Person 2", "data": [0,0,2] }, { "label": "Okta Admin Console", "type": "bar", "stack": "Person 2", "data": [0,0,0] }, { "label": "PacificTimeSheet", "type": "bar", "stack": "Person 2", "data": [0,0,1] }, { "label": "ArgoCD - Dev", "type": "bar", "stack": "Person 2", "data": [0,0,0] }, { "label": "kubero", "type": "bar", "stack": "Person 2", "data": [0,0,0] }, { "label": "Okta Dashboard", "type": "bar", "stack": "Person 2", "data": [0,0,0] }, { "label": "ArgoCD - Production IPV6", "type": "bar", "stack": "Person 2", "data": [0,0,0] }, { "label": "Okta Browser Plugin", "type": "bar", "stack": "Person 2", "data": [0,0,0] }, { "label": "Vercel", "type": "bar", "stack": "Person 2", "data": [0,0,0] }, { "label": "ONEDigital Portal Prod", "type": "bar", "stack": "Person 2", "data": [0,0,0] }, { "label": "Others", "type": "bar", "stack": "Person 3", "data": [18,0,217] }, { "label": "AWS", "type": "bar", "stack": "Person 3", "data": [3,0,17] }, { "label": "Github Enterprise Cloud - Organization", "type": "bar", "stack": "Person 3", "data": [1,0,7] }, { "label": "Okta Admin Console", "type": "bar", "stack": "Person 3", "data": [0,0,11] }, { "label": "PacificTimeSheet", "type": "bar", "stack": "Person 3", "data": [0,0,2] }, { "label": "ArgoCD - Dev", "type": "bar", "stack": "Person 3", "data": [0,0,0] }, { "label": "kubero", "type": "bar", "stack": "Person 3", "data": [4,0,0] }, { "label": "Okta Dashboard", "type": "bar", "stack": "Person 3", "data": [2,0,4] }, { "label": "ArgoCD - Production IPV6", "type": "bar", "stack": "Person 3", "data": [0,0,0] }, { "label": "Okta Browser Plugin", "type": "bar", "stack": "Person 3", "data": [3,0,3] }, { "label": "Vercel", "type": "bar", "stack": "Person 3", "data": [0,0,0] }, { "label": "ONEDigital Portal Prod", "type": "bar", "stack": "Person 3", "data": [0,0,0] } ]
初始代码
const parsed = { labelsX2: ["May", "June", "July"], labelsX: ["Person 1", "Person 2", "Person 3", "Person 1", "Person 2", "Person 3", "Person 1", "Person 2", "Person 3"], // example names but should be according json stacks datasets: json.map((item) => { console.info("item: ", item); const { label, data, stack } = item; return { ...item, label, data, stack }; }), }; const [chartData, setChartData] = React.useState(parsed); const options = { plugins: { tooltip: { title: "Total", }, subtitle: { display: true, text: "Number of events", position: 'left' }, legend: { display: true, position: 'right', }, }, scales:{ x: { type: 'category', stacked: true, labels: parsed.labelsX, }, x2: { type: 'category', stacked: true, labels: parsed.labelsX2, }, y: { stacked: true, }, } }; // ... <div className="chart-container"> <h2 style={{textAlign: "center"}}>Bar Chart</h2> <Bar data={chartData} options={options} /> </div>
问题分析与修正方案
核心问题
- 数据结构不匹配:原代码未将人员数据与月份分组对应,导致双轴对齐错误
- 轴配置缺失:双X轴未设置位置、分组绑定关系,无法实现上层月份轴与下层人员轴的关联
- Tooltip逻辑错误:未根据数据位置匹配对应的月份和人员信息
修正后的完整代码
import React from 'react'; import { Bar } from 'react-chartjs-2'; import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, Subtitle } from 'chart.js'; ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, Subtitle); // 此处插入你的JSON数据 const json = [/* 上面的JSON数据 */]; const DualXStackedBarChart = () => { // 定义轴标签 const monthLabels = ["May", "June", "July"]; const personLabels = ["Person 1", "Person 2", "Person 3"]; // 生成底层X轴的完整标签:每个月份对应3个人员 const baseXLabels = monthLabels.flatMap(() => personLabels); // 转换数据集:将每个人员的月份数据展开为按[May-P1, May-P2, May-P3, June-P1,...]排序的数组 const datasets = json.map(item => { const { label, stack, data } = item; const personIndex = personLabels.indexOf(stack); // 按月份分组展开数据 const transformedData = monthLabels.flatMap((_, monthIdx) => data[monthIdx]); return { label, type: 'bar', stack, data: transformedData, // 随机生成区分颜色(可自定义) backgroundColor: `rgba(${Math.floor(Math.random()*255)}, ${Math.floor(Math.random()*255)}, ${Math.floor(Math.random()*255)}, 0.7)` }; }); const chartData = { labels: baseXLabels, datasets }; const options = { plugins: { tooltip: { callbacks: { // 自定义Tooltip标题,显示当前数据对应的月份和人员 title: context => { const dataIdx = context[0].dataIndex; const monthIdx = Math.floor(dataIdx / personLabels.length); const personIdx = dataIdx % personLabels.length; return `${monthLabels[monthIdx]} - ${personLabels[personIdx]}`; }, label: context => `${context.dataset.label}: ${context.raw}` } }, subtitle: { display: true, text: "Number of events", position: 'left' }, legend: { display: true, position: 'right' } }, scales: { x: { type: 'category', stacked: true, ticks: { // 仅显示人员标签,每个月份下重复显示 callback: value => personLabels[value % personLabels.length] }, grid: { display: false, offset: true // 开启分组偏移,让每个月份的柱子成为一组 } }, x2: { type: 'category', stacked: true, position: 'top', offset: true, ticks: { // 仅在每个月份分组的起始位置显示月份标签 callback: value => value % personLabels.length === 0 ? monthLabels[Math.floor(value / personLabels.length)] : '' }, grid: { display: false }, // 绑定到底层X轴,确保对齐 axis: 'x', alignToPixels: true }, y: { stacked: true, title: { display: true, text: '事件数量' } } } }; return ( <div className="chart-container"> <h2 style={{ textAlign: "center" }}>双X轴堆叠柱状图</h2> <Bar data={chartData} options={options} height={400} /> </div> ); }; export default DualXStackedBarChart;
关键修正点
- 数据转换:将原数据集的
[月份1数据, 月份2数据, 月份3数据]结构,转换为按[五月-P1, 五月-P2, 五月-P3, 六月-P1,...]顺序排列的数组,匹配双轴的分组逻辑 - 轴配置优化:
- 下层X轴显示人员标签,通过
grid.offset实现月份分组 - 上层X轴设置在顶部,仅在每个分组起始位置显示月份标签,并绑定到底层X轴确保对齐
- 下层X轴显示人员标签,通过
- Tooltip增强:自定义Tooltip内容,显示当前数据对应的具体月份和人员信息
内容的提问来源于stack exchange,提问作者Gabriel Angelo
相关产品推荐
相关产品推荐

