D3.js新手求助:如何将v3重叠柱状图示例升级至v4?
适配D3.js v4实现重叠(分组)柱状图的解决方案
我明白作为D3初学者,从v3迁移到v4时遇到API变化的困扰——毕竟v4对很多核心模块做了重构,尤其是比例尺和坐标轴部分。针对你提到的v3示例,我帮你把它适配到v4版本,同时解决添加第二个柱子的问题。
首先先梳理v3到v4的核心差异点,这也是你遇到问题的主要原因:
- 比例尺重构:v3的
ordinal比例尺在v4中变为scaleBand,linear比例尺变为scaleLinear,API调用方式完全不同 - 坐标轴重命名:v3的
d3.svg.axis被拆分为方向明确的d3.axisBottom、d3.axisLeft等 - 分组柱状图的x偏移计算:v3中用
rangeRoundBands的bandwidth配合偏移,v4中用scaleBand的bandwidth()方法来计算每个柱子的宽度和偏移
接下来是完整的适配代码(数据沿用你参考的v3示例):
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script> <style> .bar1 { fill: steelblue; } .bar2 { fill: orange; } .axis { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } </style> </head> <body> <svg width="600" height="400"></svg> <script> // 参考v3示例的数据 var data = [ {category: "A", value1: 10, value2: 15}, {category: "B", value1: 20, value2: 18}, {category: "C", value1: 15, value2: 22}, {category: "D", value1: 25, value2: 19}, {category: "E", value1: 18, value2: 24} ]; var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 600 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; // --- 比例尺修改:v3 -> v4 --- // x轴比例尺:ordinal -> scaleBand var x = d3.scaleBand() .domain(data.map(function(d) { return d.category; })) .range([0, width]) .padding(0.1); // 替代v3的rangeRoundBands里的padding参数 // y轴比例尺:linear -> scaleLinear var y = d3.scaleLinear() .domain([0, d3.max(data, function(d) { return Math.max(d.value1, d.value2); })]) .range([height, 0]); // --- 坐标轴修改:v3 -> v4 --- var xAxis = d3.axisBottom(x); // 替代v3的d3.svg.axis().scale(x).orient("bottom") var yAxis = d3.axisLeft(y); var svg = d3.select("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // 添加x轴 svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); // 添加y轴 svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Value"); // --- 处理第一个柱子 --- svg.selectAll(".bar1") .data(data) .enter().append("rect") .attr("class", "bar1") // 计算x位置:scaleBand的x(d.category)是分组起始位置,加上偏移的一半 .attr("x", function(d) { return x(d.category) + x.bandwidth()/4; }) .attr("width", x.bandwidth()/2) // 每个柱子占分组宽度的一半 .attr("y", function(d) { return y(d.value1); }) .attr("height", function(d) { return height - y(d.value1); }); // --- 处理第二个柱子(解决你遇到的添加问题)--- svg.selectAll(".bar2") .data(data) .enter().append("rect") .attr("class", "bar2") // 第二个柱子的x位置:分组起始位置 + 3/4带宽,和第一个柱子并排 .attr("x", function(d) { return x(d.category) + x.bandwidth()*3/4; }) .attr("width", x.bandwidth()/2) .attr("y", function(d) { return y(d.value2); }) .attr("height", function(d) { return height - y(d.value2); }); </script> </body> </html>
关键修改说明
比例尺适配:
- 用
d3.scaleBand()替代v3的d3.scale.ordinal(),通过.range()设置范围,.padding()控制分组间距 - 用
d3.scaleLinear()替代v3的d3.scale.linear(),API逻辑类似,但命名更规范
- 用
坐标轴适配:
- 直接使用
d3.axisBottom(x)生成底部坐标轴,替代v3中.orient("bottom")的写法,语义更清晰
- 直接使用
重叠(分组)柱子的位置计算:
- 每个分组的宽度由
x.bandwidth()获取,我们把每个分组分成两部分,给两个柱子各分配一半宽度 - 第一个柱子的x位置是
x(d.category) + x.bandwidth()/4(留出1/4的间距),第二个柱子是x(d.category) + x.bandwidth()*3/4,这样两个柱子在同一分组内并排,实现“重叠”(分组)效果
- 每个分组的宽度由
如果你之前的问题是第二个柱子无法正确显示,大概率是没有正确计算v4中scaleBand的带宽和偏移——v3的ordinal.rangeRoundBands()返回的带宽计算方式和v4的scaleBand.bandwidth()略有不同,需要调整偏移值。
内容的提问来源于stack exchange,提问作者Bellice
相关产品推荐
相关产品推荐

