<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>双层径向柱状图</title>
<!-- <script src="d3.js"></script>--><script src="https://d3js.org/d3.v7.min.js"></script><style>.bar-inner:hover, .bar-outer:hover { opacity: 0.7; }.tooltip {position: absolute;padding: 8px;background: rgba(0, 0, 0, 0.8);color: white;border-radius: 4px;pointer-events: none;}</style>
</head>
<body>
<div id="chart"></div><script>// 1. 定义参数const width = 800,height = 800,innerRadius = 80, // 最内层圆半径middleRadius = 200, // 内层柱子外半径outerRadius = 350; // 外层柱子外半径// 2. 创建SVG容器const svg = d3.select("#chart").append("svg").attr("width", width).attr("height", height).append("g").attr("transform", `translate(${width/2}, ${height/2})`);// 3. 生成双层数据(示例:12个月份,内外层各一组值)const data = d3.range(12).map(i => ({month: i + 1,innerValue: Math.random() * 80 + 20, // 内层数据outerValue: Math.random() * 120 + 30 // 外层数据}));// 4. 角度比例尺(共用)const x = d3.scaleBand().domain(data.map(d => d.month)).range([-3*Math.PI/4, 3*Math.PI/4])// .padding(0.2);// 5. 定义内外层径向比例尺const yInner = d3.scaleRadial().domain([0, d3.max(data, d => d.innerValue)]).range([innerRadius, middleRadius]);const yOuter = d3.scaleRadial().domain([0, d3.max(data, d => d.outerValue)]).range([middleRadius + 20, outerRadius]); // 中间留20px空隙// 6. 绘制中心圆svg.append("circle").attr("r", innerRadius - 5).attr("fill", "#f0f0f0").attr("stroke", "#999");// 7. 创建弧形生成器(内层和外层)const arcInner = d3.arc().innerRadius(innerRadius).outerRadius(d => yInner(d.innerValue)).startAngle(d => x(d.month)).endAngle(d => x(d.month) + x.bandwidth()).padAngle(0.01);const arcOuter = d3.arc().innerRadius(middleRadius + 20) // 与外层比例尺的range起始值对齐.outerRadius(d => yOuter(d.outerValue)).startAngle(d => x(d.month)).endAngle(d => x(d.month) + x.bandwidth()).padAngle(0.01);// 8. 绘制内层柱子svg.selectAll(".bar-inner").data(data).join("path").attr("class", "bar-inner").attr("d", arcInner).attr("fill", (d, i) => d3.interpolateBlues(i/12 * 0.8));svg.append('path').attr("d",d3.arc().innerRadius(innerRadius) // 与外层比例尺的range起始值对齐.outerRadius(d => yInner(40)).startAngle(d => -4*Math.PI/5).endAngle(d => -99*Math.PI/100)).attr("fill", "grey");svg.append('path').attr("d",d3.arc().innerRadius(innerRadius) // 与外层比例尺的range起始值对齐.outerRadius(d => yInner(30)).startAngle(d => 4*Math.PI/5).endAngle(d => 99*Math.PI/100)).attr("fill", "green");// 9. 绘制外层柱子svg.selectAll(".bar-outer").data(data).join("path").attr("class", "bar-outer").attr("d", arcOuter).attr("fill", (d, i) => d3.interpolateReds(i/12 * 0.8));// 11. 工具提示const tooltip = d3.select("body").append("div").attr("class", "tooltip");function showTooltip(event, d, layer) {tooltip.style("opacity", 1).html(`${layer} Layer<br>Month ${d.month}<br>Value: ${d[layer === 'Inner' ? 'innerValue' : 'outerValue'].toFixed(1)}`).style("left", (event.pageX + 10) + "px").style("top", (event.pageY - 28) + "px");}d3.selectAll(".bar-inner").on("mouseover", function(event, d) { showTooltip(event, d, 'Inner'); }).on("mouseout", () => tooltip.style("opacity", 0));d3.selectAll(".bar-outer").on("mouseover", function(event, d) { showTooltip(event, d, 'Outer'); }).on("mouseout", () => tooltip.style("opacity", 0));</script>
</body>
</html>