Chart.js单条堆叠条形图分段间距与底部括号标签实现方法
Chart.js 单条横向堆叠条形图调整方案
堆叠分段间隙实现
Chart.js 原生无堆叠分段间隙的直接配置项,采用透明占位数据集的方式实现最简便:
- 预先定义间隙占总长度的比例(建议设为0.005~0.01,可按需调整)
- 在每两个彩色业务分段之间插入1个背景透明的占位数据集,数值为定义好的间隙宽度
- 调整各段圆角规则:仅最左侧分段保留左侧圆角、最右侧分段保留右侧圆角,中间业务分段去掉两端圆角,透明占位段不设置圆角,避免间隙处露出多余边角
下方括号分类标注实现
通过Chart.js自定义插件能力,在图表渲染完成后用Canvas原生API绘制括号和标签,无需引入额外依赖:
- 在插件的
afterDraw生命周期钩子中,先计算每个分类区间(低区间、目标区间、高区间)对应的Canvas像素起止坐标 - 调用Canvas路径API绘制对应样式的括号轮廓
- 在每个括号的居中下方位置绘制分类文字,设置匹配的字号、颜色即可
调整后完整代码
JS部分
// 各段长度定义 var len_one = 0.02; var len_two = 0.05; var len_three = 0.72; var len_four = 0.16; var len_five = 0.05; // 分段间隙宽度 const gap = 0.008; // 自定义括号标注插件 const bracketLabelPlugin = { id: 'bracketLabel', afterDraw: (chart) => { const ctx = chart.ctx; const xAxis = chart.scales.x; const yPos = chart.chartArea.bottom + 15; const bracketHeight = 8; ctx.save(); ctx.strokeStyle = '#666'; ctx.fillStyle = '#666'; ctx.font = '14px sans-serif'; ctx.textAlign = 'center'; ctx.lineWidth = 2; // 分组配置:[起始占比, 结束占比, 标签文本] const groups = [ [0, len_one + gap + len_two, 'Low'], [len_one + gap + len_two + gap, len_one + gap + len_two + gap + len_three, 'Target'], [len_one + gap + len_two + gap + len_three + gap, 1, 'High'] ]; groups.forEach(([start, end, label]) => { const xStart = xAxis.getPixelForValue(start); const xEnd = xAxis.getPixelForValue(end); // 绘制方括号 ctx.beginPath(); // 左竖线 ctx.moveTo(xStart, yPos); ctx.lineTo(xStart, yPos + bracketHeight); // 中间横线 ctx.moveTo(xStart, yPos); ctx.lineTo(xEnd, yPos); // 右竖线 ctx.moveTo(xEnd, yPos); ctx.lineTo(xEnd, yPos + bracketHeight); ctx.stroke(); // 绘制标签 ctx.fillText(label, (xStart + xEnd)/2, yPos + bracketHeight + 18); }) ctx.restore(); } } const ctx = document.getElementById("myChart").getContext('2d'); const myChart = new Chart(ctx, { type: 'bar', data: { labels: [0], datasets: [ { label: 'red-low', data: [len_one], backgroundColor: '#ab3025', barThickness: 50, borderSkipped: false, borderRadius: { bottomLeft: 3, topLeft: 3, topRight: 0, bottomRight: 0 }, }, // 间隙占位 { data: [gap], backgroundColor: 'transparent', barThickness: 50, }, { label: 'rose-low', data: [len_two], backgroundColor: '#d59792', barThickness: 50, borderSkipped: false, borderRadius: 0, }, // 间隙占位 { data: [gap], backgroundColor: 'transparent', barThickness: 50, }, { label: 'Target', data: [len_three], backgroundColor: '#44c973', barThickness: 50, borderSkipped: false, borderRadius: 0, }, // 间隙占位 { data: [gap], backgroundColor: 'transparent', barThickness: 50, }, { label: 'high-yellow', data: [len_four], backgroundColor: '#f9c63d', barThickness: 50, borderSkipped: false, borderRadius: 0, }, // 间隙占位 { data: [gap], backgroundColor: 'transparent', barThickness: 50, }, { label: 'high-orange', data: [len_five], backgroundColor: '#f5a000', barThickness: 50, borderSkipped: false, borderRadius: { bottomLeft: 0, topLeft: 0, topRight: 3, bottomRight: 3 }, } ] }, options: { indexAxis: 'y', events: [], maintainAspectRatio: false, layout: { padding: { top: 20, bottom: 60, left:20, right:20 }, }, plugins: { legend: { display: false, } }, scales: { x: { stacked: true, display: false, max: 1 }, y: { beginAtZero: true, stacked: true, display: false }, }, }, plugins: [bracketLabelPlugin] });
HTML部分
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous"> <style> #myChart { height: 180px; } </style> </head> <body> <!-- 时间范围展示图表 --> <div class="col-6 offset-3 my-5" id="time_in_range"> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script> </body> </html>
调整说明
- 可修改
gap变量值调整分段间隙宽度,值越大间隙越宽 - 可修改自定义插件中的
bracketHeight、font、颜色值调整括号和标签的样式 - 如果需要花括号样式,只需要修改插件中绘制路径的逻辑即可
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

