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
相关产品推荐
相关产品推荐

