D3.js拖拽节点时旧链接未清理,新链接重复创建问题
问题解决:拖拽节点时链接未更新且旧路径残留
你的问题出在每次调用update_links时,都在.lines组里追加新的path元素,旧的path一直留在DOM中,导致看起来线条没更新(实际是新线条叠在旧线条上)。虽然用了join处理.lines这个group本身,但join不会自动清理它的子元素。
这里给你两种解决方案:
方案一:直接更新已有链接(高效推荐)
初始化时一次性创建所有需要的path,后续更新只修改它们的d属性,避免重复创建元素:
svg_arc() function svg_arc() { var svg = d3.select('body') .append('svg') .attr('width',410) .attr('height',500) .style('background','#ececec') var g = svg.append('g') .attr('stroke','black') .attr('stroke-width',1) .attr('fill','black') var data = [ { id:"pointA", x:100, y:350, r:6, text:'A' },{ id:"pointB", x:250, y:50, r:6, text:'B' },{ id:"pointC", x:400, y:350, r:6, text:'C' } ] var node = g.selectAll('.node') .data(data) .join('g') .attr('class','node') .attr('transform',d => `translate(${d.x},${d.y})`) .attr("pointer-events", "all") var circle = node.append('circle') .attr('id',d => d.id) .attr('cx',0) .attr('cy',0) .attr('r',d => d.r) .attr("pointer-events", "all") var text = node.append('text') .attr('class','text') .text(d => d.text) .attr('x',0) .attr('y',0) .attr('dx',-15) .attr('dy',-15) node.call( d3.drag() .on("start",function(event,d) { d3.select(this).raise().classed("active", true); }) .on("drag",function(event,d) { d.x += event.dx d.y += event.dy update_links(g,data) d3.select(this).attr('transform',`translate(${d.x},${d.y})`) }) .on("end", function dragEnd(event,d) { d3.select(this).classed("active", false); }) ) // 初始化链接组和所有path var linksGroup = g.append('g').attr('class','lines'); linksGroup.append('path').attr('class','AB').attr('stroke','black'); linksGroup.append('path').attr('class','BC').attr('stroke','black'); linksGroup.append('path').attr('class','AC').attr('stroke','black').attr('fill','none'); linksGroup.append('path').attr('class','MID').attr('fill','none'); update_links(g,data) } function update_links(g,n) { var linksGroup = g.select('.lines'); // 更新每条链接的路径 linksGroup.select('.AB') .attr('d',['M',n[0].x,n[0].y,'L',n[1].x,n[1].y].join(' ')) linksGroup.select('.BC') .attr('d',['M',n[1].x,n[1].y,'L',n[2].x,n[2].y].join(' ')) linksGroup.select('.AC') .attr('d',['M',n[0].x,n[0].y,'Q',n[1].x,n[1].y,n[2].x,n[2].y].join(' ')) linksGroup.select('.MID') .attr('d',['M',(n[0].x+n[1].x)/2,(n[0].y+n[1].y)/2,'L',(n[1].x+n[2].x)/2,(n[1].y+n[2].y)/2].join(' ')) }
方案二:用D3的join管理链接元素
定义链接数据,通过join自动创建/更新path,避免重复添加:
svg_arc() function svg_arc() { var svg = d3.select('body') .append('svg') .attr('width',410) .attr('height',500) .style('background','#ececec') var g = svg.append('g') .attr('stroke','black') .attr('stroke-width',1) .attr('fill','black') var data = [ { id:"pointA", x:100, y:350, r:6, text:'A' },{ id:"pointB", x:250, y:50, r:6, text:'B' },{ id:"pointC", x:400, y:350, r:6, text:'C' } ] var node = g.selectAll('.node') .data(data) .join('g') .attr('class','node') .attr('transform',d => `translate(${d.x},${d.y})`) .attr("pointer-events", "all") var circle = node.append('circle') .attr('id',d => d.id) .attr('cx',0) .attr('cy',0) .attr('r',d => d.r) .attr("pointer-events", "all") var text = node.append('text') .attr('class','text') .text(d => d.text) .attr('x',0) .attr('y',0) .attr('dx',-15) .attr('dy',-15) node.call( d3.drag() .on("start",function(event,d) { d3.select(this).raise().classed("active", true); }) .on("drag",function(event,d) { d.x += event.dx d.y += event.dy update_links(g,data) d3.select(this).attr('transform',`translate(${d.x},${d.y})`) }) .on("end", function dragEnd(event,d) { d3.select(this).classed("active", false); }) ) update_links(g,data) } // 定义链接数据,每条链接对应一个配置 var linkData = [ { class: 'AB', getD: (n) => ['M',n[0].x,n[0].y,'L',n[1].x,n[1].y].join(' '), stroke: 'black', fill: 'black' }, { class: 'BC', getD: (n) => ['M',n[1].x,n[1].y,'L',n[2].x,n[2].y].join(' '), stroke: 'black', fill: 'black' }, { class: 'AC', getD: (n) => ['M',n[0].x,n[0].y,'Q',n[1].x,n[1].y,n[2].x,n[2].y].join(' '), stroke: 'black', fill: 'none' }, { class: 'MID', getD: (n) => ['M',(n[0].x+n[1].x)/2,(n[0].y+n[1].y)/2,'L',(n[1].x+n[2].x)/2,(n[1].y+n[2].y)/2].join(' '), stroke: 'black', fill: 'none' }, ]; function update_links(g,n) { // 管理lines组 var lines = g.selectAll('.lines') .data([0]) .join('g') .attr('class','lines') // 用join管理path元素,根据class匹配确保不会重复创建 var paths = lines.selectAll('path') .data(linkData, d => d.class) .join('path') .attr('class', d => d.class) .attr('stroke', d => d.stroke) .attr('fill', d => d.fill); // 更新所有path的路径 paths.attr('d', d => d.getD(n)); }
两种方案都能解决旧路径残留的问题,方案一更直接高效,适合固定数量的链接;方案二更灵活,适合链接数量可能变化的场景。
内容的提问来源于stack exchange,提问作者lucky1928
相关产品推荐
相关产品推荐

