如何在D3.js中实现环形(Arc)的重叠偏移效果?
问题描述
我正在尝试用D3.js创建环形组件,需求是当环形(Arc)完成整圈绘制后,产生小幅偏移,呈现环形自身重叠的视觉效果。目前新生成的弧无法与原有弧衔接,效果不符合预期。
效果对比
- 预期效果:

- 当前效果:

实现代码
JavaScript代码
/* Define properties */ const width = 500; const height = 500; const margin = 40; const circleStroke = 50; const circleSpace = 2; const radius = (width - margin) / 2; const fullCircle = Math.PI * 2; const configurations = [ {color: 'hotpink',percentage:0.5}, {color: 'limegreen',percentage:0.6}, {color: 'turquoise',percentage:0.9} ]; /* Add SVG inside <div id="activity"></div> */ const canvas = d3.select('#activity') .append('svg') .attr('width', width) .attr('height', height) /* Chart configuration. */ filter = canvas.append("defs") .append("filter") .attr("id", "drop-shadow") .attr("height", "130%"); // Adjust this value to control the shadow spread filter.append("feGaussianBlur") .attr("in", "SourceAlpha") .attr("stdDeviation", 5); // Adjust the stdDeviation value to control the blur intensity filter.append("feOffset") .attr("dx", 0) .attr("dy", 5) .attr("result", "offsetblur"); var feMerge = filter.append("feMerge"); feMerge.append("feMergeNode"); feMerge.append("feMergeNode") .attr("in", "SourceGraphic"); const rings = canvas.append('g') .attr('transform', `translate(${width / 2}, ${height / 2})`); /* Chart configuration. */ arcs = []; configurations.forEach((ring,index) =>{ var arc = d3.arc() .innerRadius(radius - circleStroke * (index + 1) - circleSpace * index) .outerRadius(radius - circleStroke * index - circleSpace * index) .cornerRadius(50) .startAngle(0) .endAngle(fullCircle*ring.percentage); arcs.push(arc); // Store arc generators in the array rings.append("path") .attr("fill",ring.color) .attr("d",arc); }); // Dynamically chaninging the the value of the rings // Array of input elements const inputElements = [ document.getElementById("ring1"), document.getElementById("ring2"), document.getElementById("ring3") ]; inputElements.forEach((inputElement,index)=>{ inputElement.addEventListener("input",()=>{ var ringPercentage = +(inputElement.value/100); arcs[index].endAngle(fullCircle * ringPercentage); // Update the end angle of the first arc console.log(ringPercentage); rings.select(`path:nth-child(${index + 1})`) .attr("fill",configurations[index].color) .style("filter", "url(#drop-shadow)") .attr("d", arcs[index]); // console.log(arcs[index].endAngle()); if(ringPercentage==1){ // create and append a new path to the ring rings.append("path"); } if(ringPercentage>1){ // create a new arc var arc_new = d3.arc() .innerRadius(radius - circleStroke * (index + 1) - circleSpace * index) .outerRadius(radius - circleStroke * index - circleSpace * index) .cornerRadius(50) .startAngle(0) .endAngle(fullCircle*(ringPercentage-1)); // get only needed path by 1 ring => 4, 2 ring=> 5,3 ring 6 switch (inputElement.id) { case "ring1": rings.select(`path:nth-child(4)`) .attr("fill",configurations[index].color) .attr("d", arc_new) .style("filter", "url(#drop-shadow)"); break; case "ring2": rings.select(`path:nth-child(5)`) .attr("fill",configurations[index].color) .attr("d", arc_new) .style("filter", "url(#drop-shadow)"); break; case "ring3": rings.select(`path:nth-child(6)`) .attr("fill",configurations[index].color) .attr("d", arc_new) .style("filter", "url(#drop-shadow)"); break; } } }) })
HTML代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Ring v2</title> <script src="https://d3js.org/d3.v7.min.js"></script> </head> <body> <div> <label for="ring1">Ring 1:</label> <input type="range" id="ring1" class="ring-range" min="0" max="150" step="1" value="70" /> </div> <div> <label for="ring2">Ring 2:</label> <input type="range" id="ring2" class="ring-range" min="0" max="100" step="1" value="90" /> </div> <div> <label for="ring3">Ring 3:</label> <input type="range" id="ring3" class="ring-range" min="0" max="150" step="1" value="40" /> </div> <div id="activity"></div> <script src="test.js"></script> </body> </html>
解决方案
问题核心是新增弧未做偏移处理,且路径选择逻辑依赖硬编码索引,导致衔接失效。以下是针对性修改:
1. 给重叠弧添加偏移
当百分比超过100%时,新增弧需要设置微小偏移(如x轴偏移2px),同时主弧保持完整一圈角度:
// 修正主弧角度:超过100%时保持整圈 const mainEndAngle = ringPercentage >= 1 ? fullCircle : fullCircle * ringPercentage; arcs[index].endAngle(mainEndAngle); rings.select(`path:nth-child(${index + 1})`) .attr("d", arcs[index]); // 处理重叠弧 if(ringPercentage > 1){ let overlayPath = rings.select(`#overlay-${index}`); // 不存在则创建 if(overlayPath.empty()){ overlayPath = rings.append("path") .attr("id", `overlay-${index}`) .attr("fill", configurations[index].color) .style("filter", "url(#drop-shadow)"); } // 生成重叠弧并添加偏移 var arc_new = d3.arc() .innerRadius(radius - circleStroke * (index + 1) - circleSpace * index) .outerRadius(radius - circleStroke * index - circleSpace * index) .cornerRadius(50) .startAngle(0) .endAngle(fullCircle*(ringPercentage-1)); overlayPath.attr("d", arc_new) .attr("transform", "translate(2, 0)"); // 偏移量可根据需求调整 } else { // 百分比低于100%时移除重叠弧 rings.select(`#overlay-${index}`).remove(); }
2. 替换硬编码路径选择
用id替代nth-child索引,避免路径顺序变化导致选择错误,代码中已通过#overlay-${index}实现精准定位。
3. 移除冗余逻辑
删除ringPercentage==1时创建空路径的代码,改为在百分比超过100%时按需创建重叠弧,减少不必要的DOM操作。
内容的提问来源于stack exchange,提问作者Uzumaki07
相关产品推荐
相关产品推荐

