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

D3堆叠面积图x轴显示月份、数组加载数据及图例提示框问题求助

D3堆叠面积图可运行实现代码

核心修复说明

  • 修复MNaN报错:原数据中所有分类数值为字符串格式,代码中先统一转换为数字类型
  • 直接使用内存数组加载数据,无需依赖外部.csv/.json等文件
  • 实现要求格式的tooltip:分类名\n该分类总计\n该分类占比
  • 图例功能正常,支持hover高亮对应面积区域
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>堆叠面积图</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        .area {
            opacity: 0.8;
        }
        .area:hover {
            opacity: 1;
        }
        .tooltip {
            position: absolute;
            background-color: rgba(0,0,0,0.8);
            color: white;
            padding: 8px 12px;
            border-radius: 4px;
            font-size: 14px;
            pointer-events: none;
            display: none;
        }
        .legend-item {
            cursor: pointer;
            font-size: 13px;
        }
    </style>
</head>
<body>
    <div id="stacked-area-chart"></div>
    <div class="tooltip"></div>

    <script>
        // 原始业务数据
        var data = [
            {"Month": "Jan", "A": "372290.4", "B": "9609.42", "C": "5496.94", "D": "4478", "E": "508005.08", "F": "188776.25", "G": "6637.87", "H": "0", "I": "109.36", "J": "357361.55", "Total": "1452764.87"},
            {"Month": "Feb", "A": "739822.97", "B": "19218.84", "C": "12516.24", "D": "8956", "E": "1018268.66", "F": "377525.74", "G": "15948.5", "H": "23.46", "I": "969.92", "J": "714723.1", "Total": "2907973.43"},
            {"Month": "Mar", "A": "1108095.89", "B": "28828.26", "C": "22268.98", "D": "13434", "E": "1528299.3", "F": "565977.38", "G": "28712.76", "H": "1027.62", "I": "3067.22", "J": "1072084.65", "Total": "4371796.06"},
            {"Month": "Apr", "A": "1474738.92", "B": "38727.73", "C": "34389.72", "D": "17981.44", "E": "2071177.85", "F": "754827.43", "G": "45430.62", "H": "7307.35", "I": "7312.26", "J": "1429446.2", "Total": "5881339.52"},
            {"Month": "May", "A": "1834106.67", "B": "49394.68", "C": "52238.77", "D": "23057.93", "E": "2585714.03", "F": "931441.48", "G": "66322.02", "H": "20124.51", "I": "13769.13", "J": "1786807.75", "Total": "7362976.97"},
            {"Month": "Jun", "A": "2186201.94", "B": "65798.97", "C": "74773.06", "D": "31084", "E": "3085654.69", "F": "1108985.53", "G": "93788.5", "H": "33374.4", "I": "25559.3", "J": "2144169.3", "Total": "8849389.69"},
            {"Month": "July", "A": "2537665.76", "B": "87416.04", "C": "107505.33", "D": "40071.93", "E": "3561821.35", "F": "1286529.58", "G": "128477.84", "H": "46917.84", "I": "39826.26", "J": "2501530.85", "Total": "10337762.78"}
        ];

        // 数据类型转换:字符串数值转数字
        const keys = ['A','B','C','D','E','F','G','H','I','J'];
        data.forEach(d => {
            keys.forEach(key => d[key] = Number(d[key]));
            d.Total = Number(d.Total);
        });

        // 画布参数
        const margin = {top: 20, right: 120, bottom: 30, left: 60},
            width = 900 - margin.left - margin.right,
            height = 500 - margin.top - margin.bottom;

        // 追加svg容器
        const svg = d3.select("#stacked-area-chart")
        .append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
        .append("g")
            .attr("transform", `translate(${margin.left},${margin.top})`);

        // 堆叠数据处理
        const stackedData = d3.stack()
            .keys(keys)
            (data);

        // X轴比例尺:月份
        const x = d3.scaleBand()
            .domain(data.map(d => d.Month))
            .range([0, width])
            .padding(0);
        
        // X轴渲染
        svg.append("g")
            .attr("transform", `translate(0, ${height})`)
            .call(d3.axisBottom(x));

        // Y轴比例尺
        const y = d3.scaleLinear()
            .domain([0, d3.max(data, d => d.Total)])
            .range([height, 0]);
        
        // Y轴渲染
        svg.append("g")
            .call(d3.axisLeft(y).ticks(6));

        // 颜色比例尺
        const color = d3.scaleOrdinal()
            .domain(keys)
            .range(d3.schemeTableau10);

        // 面积生成器
        const area = d3.area()
            .x(d => x(d.data.Month) + x.bandwidth()/2)
            .y0(d => y(d[0]))
            .y1(d => y(d[1]))
            .curve(d3.curveMonotoneX);

        // 绘制面积区域
        svg.selectAll(".area")
            .data(stackedData)
            .join("path")
                .attr("class", "area")
                .style("fill", d => color(d.key))
                .attr("d", area)
            // tooltip交互
            .on("mousemove", function(event, d) {
                const [xPos] = d3.pointer(event);
                const monthIndex = Math.min(Math.round(xPos / x.bandwidth()), data.length-1);
                const currentMonthData = data[monthIndex];
                const categoryValue = currentMonthData[d.key];
                const ratio = (categoryValue / currentMonthData.Total * 100).toFixed(2);
                d3.select(".tooltip")
                    .style("display", "block")
                    .style("left", (event.pageX + 10) + "px")
                    .style("top", (event.pageY - 10) + "px")
                    .html(`${d.key}<br/>总计:${categoryValue.toFixed(2)}<br/>占比:${ratio}%`);
            })
            .on("mouseleave", () => {
                d3.select(".tooltip").style("display", "none");
            });

        // 绘制图例
        const legend = svg.append("g")
            .attr("transform", `translate(${width + 20}, 0)`);
        
        keys.forEach((key, i) => {
            const legendRow = legend.append("g")
                .attr("class", "legend-item")
                .attr("transform", `translate(0, ${i * 25})`)
                // 图例hover高亮交互
                .on("mouseover", () => {
                    svg.selectAll(".area")
                        .style("opacity", 0.2);
                    svg.selectAll(".area")
                        .filter(d => d.key === key)
                        .style("opacity", 1);
                })
                .on("mouseleave", () => {
                    svg.selectAll(".area").style("opacity", 0.8);
                });
            
            legendRow.append("rect")
                .attr("width", 15)
                .attr("height", 15)
                .attr("fill", color(key));
            
            legendRow.append("text")
                .attr("x", 20)
                .attr("y", 12)
                .text(key);
        });
    </script>
</body>
</html>

内容的提问来源于stack exchange,提问作者Ayush sahu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 17:06:04