如何在D3.js环形径向图中实现VALUE1/VALUE2的条件填充?
解决D3.js v7环形径向图条件填充问题
问题背景
使用D3.js v7绘制基于CSV时序数据的环形径向图,核心需求:
- 当
VALUE1 > VALUE2时,对应环形区域填充蓝色 - 当
VALUE2 > VALUE1时,对应环形区域填充灰色
最初代码仅显示灰色,拆分数据绘制后出现非环形的矩形状区域,需修正以保持环形结构并实现正确的条件填充。
完整CSV数据示例
DATE_TIME,VALUE1,VALUE2 2024-01-01 00:00,10,8 2024-01-01 01:00,12,15 2024-01-01 02:00,15,11 2024-01-01 03:00,9,10 2024-01-01 04:00,14,13 2024-01-01 05:00,7,9 2024-01-01 06:00,16,12 2024-01-01 07:00,11,14 2024-01-01 08:00,18,15 2024-01-01 09:00,13,16
修正后的完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>D3.js Radial Ring Chart with Conditional Fill</title> <script src="https://d3js.org/d3.v7.min.js"></script> </head> <body> <svg width="600" height="600"></svg> <script> // 初始化SVG容器 const svg = d3.select("svg"); const width = +svg.attr("width"); const height = +svg.attr("height"); const radius = Math.min(width, height) / 2 - 40; const g = svg.append("g").attr("transform", `translate(${width/2}, ${height/2})`); // 时间解析器 const parseTime = d3.timeParse("%Y-%m-%d %H:%M"); // 加载并处理CSV数据 d3.csv("data.csv").then(data => { data.forEach(d => { d.date = parseTime(d.DATE_TIME); d.value1 = +d.VALUE1; d.value2 = +d.VALUE2; }); // 定义角度比例尺(映射时间到环形角度) const angleScale = d3.scaleTime() .domain(d3.extent(data, d => d.date)) .range([0, 2 * Math.PI]); // 定义半径比例尺(映射数值到环形半径) const radiusScale = d3.scaleLinear() .domain([0, d3.max(data, d => Math.max(d.value1, d.value2))]) .range([0, radius]); // 环形弧生成器:核心修正角度计算逻辑 const arc = d3.arc() .startAngle(d => angleScale(d.date)) .endAngle((d, i) => { // 每个弧段的结束角度取下一个时间点的角度,最后一个点循环回起始点 const nextDate = i === data.length - 1 ? data[0].date : data[i+1].date; return angleScale(nextDate); }) .innerRadius(0) .outerRadius(d => radiusScale(Math.max(d.value1, d.value2))); // 按条件拆分数据组 const value1Greater = data.filter(d => d.value1 > d.value2); const value2Greater = data.filter(d => d.value2 > d.value1); // 绘制VALUE1>VALUE2的蓝色环形区域 g.selectAll(".arc-blue") .data(value1Greater) .enter() .append("path") .attr("class", "arc-blue") .attr("d", arc) .attr("fill", "#1f77b4") .attr("opacity", 0.7); // 绘制VALUE2>VALUE1的灰色环形区域 g.selectAll(".arc-gray") .data(value2Greater) .enter() .append("path") .attr("class", "arc-gray") .attr("d", arc) .attr("fill", "#7f7f7f") .attr("opacity", 0.7); // 添加时间刻度 const timeTicks = angleScale.ticks(d3.timeHour.every(1)); g.selectAll(".time-tick") .data(timeTicks) .enter() .append("g") .attr("class", "time-tick") .attr("transform", d => `rotate(${(angleScale(d) * 180 / Math.PI) - 90}) translate(${radius},0)`) .append("text") .attr("text-anchor", "middle") .attr("transform", "rotate(90)") .text(d => d3.timeFormat("%H:%M")(d)); // 添加半径刻度环 const radiusTicks = radiusScale.ticks(5); g.selectAll(".radius-tick") .data(radiusTicks) .enter() .append("circle") .attr("class", "radius-tick") .attr("cx", 0) .attr("cy", 0) .attr("r", d => radiusScale(d)) .attr("fill", "none") .attr("stroke", "#ccc"); }); </script> </body> </html>
关键修正说明
- 环形结构修复:通过让每个弧段的
endAngle指向下一个时间点的角度,最后一个点循环回起始点,确保环形闭合,避免出现矩形状区域。 - 条件分组绘制:将数据按
VALUE1和VALUE2的大小关系拆分为两组,分别调用弧生成器绘制,保证不同条件的区域正确填充对应颜色。 - 刻度适配优化:调整时间刻度的旋转和位置,使其贴合环形图的径向布局,提升可读性。
额外提示
- 若CSV文件路径不正确,可改用
d3.csvParse()直接嵌入CSV字符串。 - 可根据需求调整颜色、透明度、刻度间隔等样式参数。
- 若存在
VALUE1=VALUE2的场景,可添加额外逻辑处理(如填充白色或跳过绘制)。
内容的提问来源于stack exchange,提问作者dsapprentice
相关产品推荐
相关产品推荐

