如何将d3可拖拽图节点绑定到矩形,实现拖动矩形时节点及连线同步移动
解决方案
你之前的方案失效主要有两个原因:
- 把节点追加到矩形或矩形分组后,你拖拽的是
rect元素本身,transform属性仅作用在单个矩形上,父容器没有发生位移,所以子节点不会同步移动 - 连线的坐标是基于SVG全局坐标系写死的,就算节点位置变了,没有手动更新连线坐标的话,连线也不会跟着动
你可以按照下面的逻辑修改代码,就能实现拖拽矩形同步带动节点和连线移动的效果:
核心修改步骤
- 先建立矩形和对应节点的绑定关系,默认按索引一一对应即可,你也可以后续根据自己的业务逻辑调整绑定规则
- 修改矩形的拖拽事件,拖拽矩形的同时同步更新绑定节点的坐标,再重绘所有连线
- 不需要调整DOM层级结构,保持现有结构就能实现效果
修改后可直接运行的完整代码
var width = 960, height = 500; var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); var graph = { "nodes": [{ "x": 170, "y": 120 }, { "x": 400, "y": 100 }], "links": [{ "source": 0, "target": 1 }] } graph.links.forEach(function (d) { d.source = graph.nodes[d.source]; d.target = graph.nodes[d.target]; }); var dataRects = [ { "x": 20, "y": 50, "width": 200, "height": 100, linkedNodeIndex: 0 }, // 绑定第一个节点 { "x": 350, "y": 75, "width": 150, "height": 50, linkedNodeIndex: 1 } // 绑定第二个节点 ] // 新增:重绘所有连线的通用方法 function updateLinks() { link.attr("x1", function (d) { return d.source.x; }) .attr("y1", function (d) { return d.source.y; }) .attr("x2", function (d) { return d.target.x; }) .attr("y2", function (d) { return d.target.y; }); } var rects = svg.append("g") .attr("id", function () { return "g_0" }) .attr("class", "rect") .selectAll("rect") .data(dataRects) .enter() .append("rect") .attr("id", function (d, i) { return "box_" + i }) .attr("x", function (d) { return d.x; }) .attr("y", function (d) { return d.y; }) .attr("width", function (d) { return d.width }) .attr("height", function (d) { return d.height }) .call(d3.drag() .on("drag", function (d) { const matrix = d3.select(this).node().transform.baseVal.consolidate().matrix; const x = matrix.e + d3.event.dx; const y = matrix.f + d3.event.dy; d3.select(this).attr("transform", `translate(${x},${y})`); // 新增:同步更新绑定节点的坐标 const linkedNode = graph.nodes[d.linkedNodeIndex]; linkedNode.x += d3.event.dx; linkedNode.y += d3.event.dy; // 更新节点DOM d3.select(`#node_${d.linkedNodeIndex}`) .attr("cx", linkedNode.x) .attr("cy", linkedNode.y); // 重绘所有连线 updateLinks(); }) .on("end", function (d) { idOnEnd = d3.select(this).attr("id"); }) ) .attr("transform", "translate(0,0)"); var link = svg.append("g") .attr("class", "link") .selectAll("line") .data(graph.links) .enter().append("line") .call(updateLinks); // 初始化调用连线渲染 var node = svg.append("g") .attr("class", "node") .selectAll("circle") .data(graph.nodes) .enter().append("circle") .attr("id", function (d, i) { return "node_" + i; }) .attr("r", 9) .attr("cx", function (d) { return d.x; }) .attr("cy", function (d) { return d.y; }) .call(d3.drag().on("drag", dragged)); function dragged(d) { d.x = d3.event.x, d.y = d3.event.y; d3.select(this).attr("cx", d.x).attr("cy", d.y); updateLinks(); // 替换原来的单独更新逻辑,直接调用通用连线更新方法 }
CSS和HTML部分不需要修改,直接复用你原来的即可。
如果后续要实现点击创建连线的功能,只需要在点击矩形的时候记录起点/终点节点,往graph.links数组里追加新的连线数据,再调用updateLinks()方法重绘即可。
内容的提问来源于stack exchange,提问作者pmeran
相关产品推荐
相关产品推荐

