D3.js v7水平堆叠条形图Y轴意外偏移问题排查
问题根源与修复方案
你的堆叠条形图出现垂直偏移的核心原因是选择器范围错误:
- 代码中
svg.selectAll('g')会选中SVG中已存在的所有<g>元素(包括你之前创建的y轴、x轴容器g),导致数据绑定逻辑完全混乱。 - 同时你没有正确使用D3的
enter()模式创建新分组元素,而是直接调用.append('g'),这会在已有g元素上继续添加内容,最终引发布局偏移。
具体修复步骤
- 限定分组选择器范围
将选择器改为针对专属类名(比如.bar-group),避免选中坐标轴的g元素,同时正确使用enter()模式创建新元素:
var groups = svg.selectAll('.bar-group') // 仅选择带bar-group类的g .data(newdata) .enter() // 仅为新增数据创建元素 .append('g') .attr('class', 'bar-group') // 给分组添加类名 .style('fill', function(d, i) { return colors(i); });
- 使用scaleBand的带宽设置条形高度
替换固定的height:10,用yScale.bandwidth()获取自动计算的带宽,适配y轴刻度:
.attr('height', yScale.bandwidth())
完整修复后的代码
<script src="https://d3js.org/d3.v7.min.js"></script> <body> <div id="bar_chart"> <script> var data = [{ dep_time: "5:30", risk: 100, details: [{ time: 19, source: 'Drive' }, { time: 10, source: 'Margin' }, { time: 42, source: 'Full' }, { time: 35, source: 'Crossing' }, { time: 23, source: 'Drive' } ] }, { dep_time: "6:20", risk: 80, details: [{ time: 25, source: 'Drive' }, { time: 1, source: 'Margin' }, { time: 38, source: 'Full' }, { time: 35, source: 'Crossing' }, { time: 25, source: 'Drive' } ] }, { dep_time: "7:10", risk: 5, details: [{ time: 8, source: 'Drive' }, { time: 28, source: 'Margin' }, { time: 38, source: 'Full' }, { time: 35, source: 'Crossing' }, { time: 18, source: 'Drive' } ] } ]; var units = []; var xMax = 0; data.forEach(function(s) { var total = 0; s.details.forEach(function(s) { s["x0"] = total; s["x"] = s.time; s["x1"] = total + s.time; total = total + s.time; if (total > xMax) xMax = total; }); s["y"] = s.dep_time; units.push(s.dep_time); }); var newdata = []; for (var i = 0; i < data[0].details.length; i++) { var row = []; data.forEach(function(s) { row.push({ x: s.details[i].x, y: s.dep_time, x0: s.details[i].x0 }); }); newdata.push(row); } var margins = { left: 50, bottom: 50, top: 25, right: 25 }; var sizes = { width: 500, height: 150 }; var width = sizes.width - margins.left - margins.right; var height = sizes.height - margins.bottom - margins.top; var svg = d3.select("#bar_chart") .append('svg') .attr('width', width + margins.left + margins.right) .attr('height', height + margins.bottom + margins.top) .append('g') .attr('transform', 'translate(' + margins.left + ', ' + margins.top + ")"); var yScale = d3.scaleBand() .domain(units) .rangeRound([0, height]) .padding(0.1); var yAxis = d3.axisLeft(yScale); var yAxisG = svg.append("g") .attr("id", "yaxis") .call(yAxis); const xScale = d3.scaleLinear() .domain([0, xMax]) .range([0, width]); var xAxis = d3.axisBottom(xScale); var xAxisG = svg.append("g") .attr("transform", "translate(0, " + height + ")") .attr("id", "xaxis") .call(xAxis.ticks(8)); var bar_colors = ['red', 'purple', 'green', 'lightblue', 'yellow']; var colors = function(i) { return bar_colors[i]; } var groups = svg.selectAll('.bar-group') .data(newdata) .enter() .append('g') .attr('class', 'bar-group') .style('fill', function(d, i) { return colors(i); }); groups.selectAll('rect') .data(function(d) { return d; }) .enter() .append('rect') .attr('x', function(d) { return xScale(d.x0); }) .attr('y', function(d) { return yScale(d.y); }) .attr('height', yScale.bandwidth()) .attr('width', function(d) { return xScale(d.x); }); </script> </div> </body>
额外说明:代码中还修正了SVG高度计算(原代码未包含top边距),并添加了可选的padding参数让条形之间有间距,优化视觉效果。
内容的提问来源于stack exchange,提问作者djmcmath
相关产品推荐
相关产品推荐

