You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将d3可拖拽图节点绑定到矩形,实现拖动矩形时节点及连线同步移动

解决方案

你之前的方案失效主要有两个原因:

  • 把节点追加到矩形或矩形分组后,你拖拽的是rect元素本身,transform属性仅作用在单个矩形上,父容器没有发生位移,所以子节点不会同步移动
  • 连线的坐标是基于SVG全局坐标系写死的,就算节点位置变了,没有手动更新连线坐标的话,连线也不会跟着动

你可以按照下面的逻辑修改代码,就能实现拖拽矩形同步带动节点和连线移动的效果:


核心修改步骤

  1. 先建立矩形和对应节点的绑定关系,默认按索引一一对应即可,你也可以后续根据自己的业务逻辑调整绑定规则
  2. 修改矩形的拖拽事件,拖拽矩形的同时同步更新绑定节点的坐标,再重绘所有连线
  3. 不需要调整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 16:36:00