You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

关键修改说明

  1. 比例尺适配:

    • 用d3.scaleBand()替代v3的d3.scale.ordinal(),通过.range()设置范围,.padding()控制分组间距
    • 用d3.scaleLinear()替代v3的d3.scale.linear(),API逻辑类似,但命名更规范
  2. 坐标轴适配:

    • 直接使用d3.axisBottom(x)生成底部坐标轴,替代v3中.orient("bottom")的写法,语义更清晰
  3. 重叠(分组)柱子的位置计算:

    • 每个分组的宽度由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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:17:29