如何基于节点ID移除D3树图两节点间路径且保留节点
移除指定ID节点间的路径(保留节点)
这是当前的树状图表代码:
HTML 代码
<!doctype html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.js"></script> </head> <body> <svg></svg> </body> </html>
JavaScript 代码
const data = { "name": "tree", "id": 0, "children": [{ "name": "A", "id": 1, "children": [{ "name": "B", "id": 2, "children": [{ "name": "C", "id": 3 }, ] }, { "name": "H", "id": 4, "children": [{ "name": "D", "id": 5 }, { "name": "E", "id": 6 }, { "name": "F", "id": 7 }, ] }, ] }] } const width = 928; // 计算树高,让SVG高度根据树的宽度自适应 const root = d3.hierarchy(data); const dx = 10; const dy = width / (root.height + 1); // 创建树布局 const tree = d3.tree().nodeSize([dx, dy]); // 排序并应用布局 root.sort((a, b) => d3.ascending(a.data.name, b.data.name)); tree(root); // 计算树的范围,这里x和y互换是因为树布局里x代表宽度,但展示时树是向右延伸而非向下 let x0 = Infinity; let x1 = -x0; root.each(d => { if (d.x > x1) x1 = d.x; if (d.x < x0) x0 = d.x; }); // 计算调整后的树高 const height = x1 - x0 + dx * 2; const svg = d3.select("svg") .attr("width", width) .attr("height", height) .attr("viewBox", [-dy / 3, x0 - dx, width, height]) .attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;"); const link = svg.append("g") .attr("fill", "none") .attr("stroke", "#555") .attr("stroke-opacity", 0.4) .attr("stroke-width", 1.5) // 生成节点间路径,新增自定义属性标记父子节点ID link .selectAll() .data(root.links()) .join("path") .attr("d", d3.linkHorizontal() .x(d => d.y) .y(d => d.x)) // 添加自定义属性,存储父节点ID和子节点ID .attr("data-link-ids", d => `${d.source.data.id}-${d.target.data.id}`); const node = svg.append("g") .attr("stroke-linejoin", "round") .attr("stroke-width", 3) .selectAll() .data(root.descendants()) .join("g") .attr("transform", d => `translate(${d.y},${d.x})`) node.append("circle") .attr("fill", d => d.children ? "#555" : "#999") .attr("r", 5.5) node.append("text") .attr("dy", "0.31em") .attr("x", d => d.children ? -6 : 6) .attr("text-anchor", d => d.children ? "end" : "start") .text(d => d.data.name) .clone(true).lower() .attr("stroke", "white"); d3.selectAll("circle").on("contextmenu", (event) => { this.selectedNode = event.srcElement.__data__.data }); const connectNodes = (t, f) => { let to = null, fr = null; node.each(d => { if (d.data.name === t) to = d; if (d.data.name === f) fr = d; }); if (to && fr) { link.append("path") .attr("d", "M" + to.y + "," + to.x + "L" + fr.y + "," + fr.x) .attr("fill", "none") .attr("stroke", "red") // 给自定义连接也添加ID标记 .attr("data-link-ids", `${to.data.id}-${fr.data.id}`); } }; connectNodes("B", "D");
需求是移除节点H(ID=4)和节点F(ID=7)之间的路径,但保留F节点,需要实现一个通过节点ID操作的函数:
function removePath(from_node_id:number, to_node_id:number){ // 实现代码 }
解决方案
我们通过给每个路径添加标记节点ID的自定义属性,就能精准匹配目标路径并移除:
function removePath(from_node_id, to_node_id) { // 拼接目标路径的ID组合 const targetLinkId = `${from_node_id}-${to_node_id}`; // 筛选对应的path元素并移除 link.selectAll(`path[data-link-ids="${targetLinkId}"]`).remove(); } // 调用示例:移除H(ID4)到F(ID7)的路径 removePath(4,7);
把这个函数加到你的JS代码里,调用removePath(4,7)就能实现需求——既移除了指定路径,又保留了F节点。
内容的提问来源于stack exchange,提问作者morteza mortezaie
相关产品推荐
相关产品推荐

