我正在尝试D3v4力仿真,但目前无法正确地向图中添加新节点。这是设置
var svgRef = d3.select("body").append("svg")
.attr("width",svgWidth)
.attr("height",svgHeight)
.on("mousedown",svgClick);
var nodes_data = [{id: "0"},{id: "1"},{id: "2"}];
var links_data = [{source:"1",target:"2"}];
var linksRef = svgRef
.selectAll(".link")
.data(links_data).enter()
.append("line").attr("class", "link");
var nodesRef = svgRef
.append("g").attr("class", "nodes")
.selectAll(".node")
.data(nodes_data).enter()
.append("g").attr("class", "node")
.call(d3.drag()...);
nodesRef.append("circle")
.attr("r", nodeRadius);
我正在为单个节点创建一个组,以容纳它们的circle
和title
和text
等,所有这些节点都在一个更大的“节点”组下创建。最终结果如下:
<svg>
...
<g class="nodes">
<g class="node">
<circle>
<text>
...
</g>
<g class="node"></g>
<g class="node"></g>
...
</g>
</svg>
然后我要在数据中添加一个新节点,然后尝试将其插入图形中
function svgClick(){
// add new node
nodes_data.push({id: nodeCount++, "x":d3.event.X, "y":d3.event.y});
// restart simulation
simulation.nodes(nodes_data).on("tick", ticked);
// insert new nodes
nodeEnter = svgRef
.selectAll(".node")
.data(nodes_data).enter()
.append("g").attr("class", "node")
.call(d3.drag()...)
.append("circle")
.attr("class", "node")
.attr("r", nodeRadius);
// merge new nodes
nodesRef = nodeEnter.merge(nodesRef);
}
我无法修复的2处出问题的地方:
插入的节点未出现在鼠标坐标上
弄清楚了这个,只是d3.event.X
中的一个错字,其中 X 必须小写
未将插入的节点添加到<g class="nodes">
中,但添加到下面。我找不到正确选择它的方法
像这样:
<svg>
<g class="nodes">
<g class="node"></g>
<g class="node"></g>
</g>
<g class="node"></g> // <--- inserted here
</svg>
答案 0 :(得分:1)
对于第二个问题:“插入的节点未添加到,而是在下面。我找不到正确选择它的方法”,如何输入新节点说明了为什么最终拥有自己的结构:
nodeEnter = svgRef
.selectAll(".node") // select all elements with class node in the svg
.data(nodes_data) // bind new data to them
.enter() // enter new nodes where needed
.append("g") // append a g for each new node needed to the svg
这会将新节点直接附加到父svg:svgRef.selectAll(...)...
。
如果要将新节点附加到父级,则必须首先选择该父级。如果我们想将g
与类nodes
一起用作新节点的父元素,我们将使用:svgRef.select(".nodes").selectAll(...)...
。
这看起来像:
nodeEnter = svgRef
.select(".nodes") // select the parent g with class nodes
.seleactAll(".node") // select all nodes in that parent
.data(nodes_data) // bind new data to them
.enter() // enter new nodes where needed
.append("g") // append a g for each new node needed to the g with class nodes
此外,您还希望nodeEnter是g
元素,而不是circle
元素,因此您可以在此处拆分链接。圆圈也不需要类节点,因为父级g具有它。
对于定位,您使用的是d3.event.X
而不是d3.event.x
,并且在codepen中,相对于问题,您使用的是节点的cx,cy属性(力将位置设为是x,y属性)。
这里是updated pen。
请记住,如果tick结束,则新节点的位置不会像放置在每个tick上一样,而不是放在新节点上(如果要确保tick,请重置alpha)。同样,您可能会看到的跳动是由于d3.forceCenter试图确保节点的重心是指定点(如果要避免该问题,可以施加定位力而不是d3.forceCenter)