如何用Chart.js实现带全宽覆盖条的动态柱状图
需求说明
- 使用Chart.js创建柱状图,将主
dataset以柱状图形式展示 - 在主
dataset上方叠加YellowBar和RedBar数据集,这两个数据集的条形需横跨整个图表宽度 YellowBar和RedBar的选项可通过下拉菜单选择,数值随选择动态更新- 主
dataset的列数会随下拉菜单选择变化
用户已实现主数据集的柱状图,但添加横跨宽度的黄/红条形时遇到问题,现有代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bar Chart with Dataset Selection</title> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.2"></script> </head> <body> <div> <canvas id="myChart" width="3" height="2"></canvas> <label for="datasetSelect">Select Map:</label> <select id="datasetSelect"> <option value="dataset1">Map #1</option> <option value="dataset2">Map #2</option> </select> </div> <div> <label for="YellowBar">Select Option #1:</label> <select id="YellowBar"> <option value="YB1">Thing #1</option> <option value="YB2">Thing #2</option> </select> </div> <div> <label for="RedBar">Select Option #2:</label> <select id="RedBar"> <option value="RB1">Thing #3</option> <option value="RB2">Thing #4</option> </select> </div> <script> document.addEventListener('DOMContentLoaded', function() { var ctx = document.getElementById('myChart').getContext('2d'); var datasets = { dataset1: { labels: ['Item A', 'Item B', 'Item C', 'Item D'], data: [[612,1836], [612,1836], [1165,3029], [1576,3636]] }, dataset2: { labels: ['Item X', 'Item Y', 'Item Z'], data: [[612,1836], [700,2100], [945,2458]] }, }; var YellowBar = { YB1: { data: [[800,1400]]}, YB2: { data: [[1000,1600]]}, }; var RedBar = { RB1: { data: [[2300,2800]]}, RB2: { data: [[2500,3000]]}, }; var chart = new Chart(ctx, { type: 'bar', data: { labels: datasets.dataset1.labels, datasets: [{ label: 'Dataset 1', data: datasets.dataset1.data, backgroundColor: 'rgba(29, 20, 124, 0.8)', borderColor: 'rgba(29, 20, 124, 0.8)', borderWidth: 1 }] }, options: { plugins: { legend: { display: false } }, scales: { x: { title: { display: true, text: 'X-axis Label' }, ticks: { autoSkip: false, maxRotation: 90, minRotation: 90 } }, y: { title: { display: true, text: 'Y-axis Label' }, ticks: { beginAtZero: true } } } } }); document.getElementById('datasetSelect').addEventListener('change', function() { var selectedDataset = this.value; chart.data.labels = datasets[selectedDataset].labels; chart.data.datasets[0].label = selectedDataset; chart.data.datasets[0].data = datasets[selectedDataset].data; chart.update(); }); }); </script> </body> </html>
实现方案
要让黄/红条形横跨整个图表宽度,需利用Chart.js的barPercentage和categoryPercentage属性将条形宽度设为100%,同时让这两个数据集的每个数据点对应相同的数值范围。以下是修改后的完整代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bar Chart with Dataset Selection</title> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.2"></script> </head> <body> <div> <canvas id="myChart" width="3" height="2"></canvas> <label for="datasetSelect">Select Map:</label> <select id="datasetSelect"> <option value="dataset1">Map #1</option> <option value="dataset2">Map #2</option> </select> </div> <div> <label for="YellowBar">Select Option #1:</label> <select id="YellowBar"> <option value="YB1">Thing #1</option> <option value="YB2">Thing #2</option> </select> </div> <div> <label for="RedBar">Select Option #2:</label> <select id="RedBar"> <option value="RB1">Thing #3</option> <option value="RB2">Thing #4</option> </select> </div> <script> document.addEventListener('DOMContentLoaded', function() { var ctx = document.getElementById('myChart').getContext('2d'); var datasets = { dataset1: { labels: ['Item A', 'Item B', 'Item C', 'Item D'], data: [[612,1836], [612,1836], [1165,3029], [1576,3636]] }, dataset2: { labels: ['Item X', 'Item Y', 'Item Z'], data: [[612,1836], [700,2100], [945,2458]] }, }; var YellowBarData = { YB1: [[800,1400]], YB2: [[1000,1600]] }; var RedBarData = { RB1: [[2300,2800]], RB2: [[2500,3000]] }; // 生成对应长度的数据集(每个元素都是相同的范围值) function generateFullWidthData(baseData, length) { return Array(length).fill(baseData[0]); } var initialLabelCount = datasets.dataset1.labels.length; var chart = new Chart(ctx, { type: 'bar', data: { labels: datasets.dataset1.labels, datasets: [ { label: 'Dataset 1', data: datasets.dataset1.data, backgroundColor: 'rgba(29, 20, 124, 0.8)', borderColor: 'rgba(29, 20, 124, 0.8)', borderWidth: 1 }, { label: 'YellowBar', data: generateFullWidthData(YellowBarData.YB1, initialLabelCount), backgroundColor: 'rgba(255, 206, 86, 0.6)', borderColor: 'rgba(255, 206, 86, 1)', borderWidth: 1, barPercentage: 1, categoryPercentage: 1 }, { label: 'RedBar', data: generateFullWidthData(RedBarData.RB1, initialLabelCount), backgroundColor: 'rgba(255, 99, 132, 0.6)', borderColor: 'rgba(255, 99, 132, 1)', borderWidth: 1, barPercentage: 1, categoryPercentage: 1 } ] }, options: { plugins: { legend: { display: false } }, scales: { x: { title: { display: true, text: 'X-axis Label' }, ticks: { autoSkip: false, maxRotation: 90, minRotation: 90 } }, y: { title: { display: true, text: 'Y-axis Label' }, ticks: { beginAtZero: true } } } } }); // 更新主数据集 document.getElementById('datasetSelect').addEventListener('change', function() { var selectedDataset = this.value; var labelCount = datasets[selectedDataset].labels.length; chart.data.labels = datasets[selectedDataset].labels; chart.data.datasets[0].label = selectedDataset; chart.data.datasets[0].data = datasets[selectedDataset].data; // 同步更新黄/红条形的数据集长度 var selectedYB = document.getElementById('YellowBar').value; chart.data.datasets[1].data = generateFullWidthData(YellowBarData[selectedYB], labelCount); var selectedRB = document.getElementById('RedBar').value; chart.data.datasets[2].data = generateFullWidthData(RedBarData[selectedRB], labelCount); chart.update(); }); // 更新YellowBar document.getElementById('YellowBar').addEventListener('change', function() { var selectedYB = this.value; var labelCount = chart.data.labels.length; chart.data.datasets[1].data = generateFullWidthData(YellowBarData[selectedYB], labelCount); chart.update(); }); // 更新RedBar document.getElementById('RedBar').addEventListener('change', function() { var selectedRB = this.value; var labelCount = chart.data.labels.length; chart.data.datasets[2].data = generateFullWidthData(RedBarData[selectedRB], labelCount); chart.update(); }); }); </script> </body> </html>
关键实现细节
- 横跨宽度设置:给黄/红数据集添加
barPercentage: 1和categoryPercentage: 1,让条形占满所属类别的全部宽度,结合每个类别都设置相同的数值范围,实现横跨整个图表的效果 - 动态数据生成:
generateFullWidthData函数根据当前主数据集的标签数量,生成对应长度的数组,每个元素都是选中的黄/红条形的数值范围 - 联动更新:主数据集切换时,同步更新黄/红条形的数据集长度;黄/红下拉菜单切换时,更新对应数据集的数值范围并刷新图表
内容的提问来源于stack exchange,提问作者grfrazee
相关产品推荐
相关产品推荐

