如何在ChartJS中实现X轴多级别分类标签并按角色分组
实现ChartJS X轴多级分组标签(含分隔线)
ChartJS原生确实没有内置多级分类X轴的功能,但通过自定义插件可以完全实现你的需求,且兼容chartjs-node-canvas服务端渲染场景。以下是具体实现步骤:
1. 预处理数据:按角色分组
先将你的用户数据按rolTitle分组,整理成「角色-用户列表」的结构,方便后续计算标签位置:
// 假设你的原始数据是这样的数组 const rawData = [ { rolTitle: "管理员", userName: "张三", value: 12 }, { rolTitle: "管理员", userName: "李四", value: 8 }, { rolTitle: "编辑", userName: "王五", value: 15 }, { rolTitle: "访客", userName: "赵六", value: 5 }, ]; // 按rolTitle分组 const groupedByRole = rawData.reduce((acc, item) => { if (!acc[item.rolTitle]) { acc[item.rolTitle] = []; } acc[item.rolTitle].push(item); return acc; }, {} as Record<string, typeof rawData>); // 提取分组后的标签和数据 const xLabels = Object.values(groupedByRole).flatMap(group => group.map(item => item.userName)); const datasetValues = rawData.map(item => item.value); // 记录每个角色对应的X轴索引范围,用于绘制分隔线和组标签 const roleRanges = Object.entries(groupedByRole).reduce((acc, [role, group], idx, arr) => { const start = idx === 0 ? 0 : arr.slice(0, idx).reduce((sum, [_, g]) => sum + g.length, 0); const end = start + group.length - 1; acc.push({ role, start, end }); return acc; }, [] as { role: string; start: number; end: number }[]);
2. 编写自定义X轴分组插件
这个插件会在X轴上方绘制组标签,并在组之间添加分隔线:
const multiLevelXAxisPlugin = { id: 'multiLevelXAxis', beforeDraw: (chart: any) => { const ctx = chart.ctx; const xAxis = chart.scales.x; const chartTop = chart.chartArea.top; const groupLabelY = chartTop - 20; // 组标签的Y坐标,可调整 // 绘制组标签 ctx.font = '14px Arial'; ctx.fillStyle = '#333'; ctx.textAlign = 'center'; roleRanges.forEach(range => { // 计算组标签的X坐标(组内所有刻度的中间位置) const startX = xAxis.getPixelForTick(range.start); const endX = xAxis.getPixelForTick(range.end); const midX = (startX + endX) / 2; ctx.fillText(range.role, midX, groupLabelY); }); // 绘制组分隔线 ctx.strokeStyle = '#ccc'; ctx.lineWidth = 1; roleRanges.forEach((range, idx) => { // 跳过最后一组的右侧分隔线 if (idx === roleRanges.length - 1) return; const lineX = xAxis.getPixelForTick(range.end) + (xAxis.getPixelForTick(range.end + 1) - xAxis.getPixelForTick(range.end)) / 2; ctx.beginPath(); ctx.moveTo(lineX, chartTop); ctx.lineTo(lineX, chart.chartArea.bottom); ctx.stroke(); }); } };
3. 配置ChartJS实例(兼容chartjs-node-canvas)
在创建图表时,注册自定义插件,并调整布局留出组标签的空间:
import { CanvasRenderService } from 'chartjs-node-canvas'; import ChartDataLabels from 'chartjs-plugin-datalabels'; const width = 800; const height = 400; const canvasRenderService = new CanvasRenderService(width, height, (Chart) => { // 注册自定义插件和datalabels插件 Chart.register(multiLevelXAxisPlugin, ChartDataLabels); }); async function generateChart() { const configuration = { type: 'bar', // 假设你用的是柱状图,其他类型同理 data: { labels: xLabels, datasets: [{ label: '用户数据', data: datasetValues, backgroundColor: '#2ecc71' }] }, options: { layout: { padding: { top: 40 // 顶部留出空间放组标签 } }, scales: { x: { ticks: { font: { size: 12 } } }, y: { beginAtZero: true } }, // 保留你原有的datalabels配置 plugins: { datalabels: { anchor: 'end', align: 'top', formatter: (value: number) => value, font: { weight: 'bold' } } } } }; const imageBuffer = await canvasRenderService.renderToBuffer(configuration); // 后续将buffer返回给Handlebars模板渲染 return imageBuffer; }
关键说明
- 社区说“无法实现”是指ChartJS原生无此功能,但自定义插件是官方支持的扩展方式,完全可行;
- 插件中的坐标计算基于ChartJS的
scales.x.getPixelForTick方法,能自动适配不同的图表尺寸和刻度位置; - 若需要调整组标签样式、分隔线样式,直接修改插件中的
ctx.font、ctx.strokeStyle等属性即可; - 该方案完全兼容
chartjs-node-canvas服务端渲染,无需额外调整。
内容的提问来源于stack exchange,提问作者Izlia
相关产品推荐
相关产品推荐

