我正在尝试创建一个d3强制图,其中动态添加(以后删除)节点。 This example大致按照我的要求行事,但我无法适应它,因为评论相当稀疏。
我的主要问题围绕start()
函数:
function start() {
link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
link.enter().insert("line", ".node").attr("class", "link");
link.exit().remove();
node = node.data(force.nodes(), function(d) { return d.id;});
node.enter().append("circle").attr("class", function(d) { return "node " + d.id; }).attr("r", 8);
node.exit().remove();
force.start();
}
首先,link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
到底在做什么?仅制作静态布局的示例使用此样式:
link = svg.selectAll("line")
.data(links)
.enter()
.append("line")
.attr("class", "link");
该代码是否与start()
中的前两行相同? (第4..6行似乎更接近这种风格,所以,为什么他们使用不同的方法(insert
与append
)?)
而且,为什么第2行在处理链接而不是节点时会引用.node
?
我的最后一个问题是,继续这样调用force.start()
是否安全?举例来说,如果start()被调用100次,相隔10ms,那会不会有问题?它是否会导致对tick功能的任何额外调用? (我只是想知道我是否应该在force.stop()
的顶部调用start()
)? (API docs对于d3.layout.force没有明确说出。)
奖金问题:这些线条实际上在做什么吗?
var node = svg.selectAll(".node"),
link = svg.selectAll(".link");
由于此时没有节点,它们是否相当于:
var node,link;
答案 0 :(得分:2)
关于您的第一个问题,此代码正在更新绑定到表示链接的DOM元素的数据。也就是说,我们采用现有的选择link
(包含到目前为止的所有链接)并在其上调用.data()
。 .data()
的第二个参数告诉D3如何确定DOM元素是否表示数据元素 - 在这种情况下,它们由源和目标ID链接。
然后将此新选择(旧选择+新数据绑定)分配回link
,以便该变量再次包含链接元素的选择,但现在包含新数据。然后,我们可以为.enter()
选择添加新元素,从.exit()
选项中删除元素并更新其余元素。
您已明确发布的静态示例中的代码再次选择所有元素(svg.selectAll("line")
),而不是使用之前的选择集。另一个区别是它仅在.enter()
选择上运行。这适用于静态版本,因为您只需添加一次链接而不修改它们,但在您的情况下,您还需要处理其他选择。
添加新链接的代码使用.insert("line", ".node")
而不是.append()
只是为了保留元素的相对顺序。 SVG元素按其定义的顺序呈现,因此在节点前面添加的链接将出现在节点前面。使用.insert("line", ".node")
,您告诉D3在第一个line
元素之前插入新的.node
元素。也就是说,在DOM中的第一个节点之前添加任何新链接,这样就不会有任何节点被一条线遮挡。
一般情况下,可以根据需要多次拨打force.start()
。在这种情况下它所做的只是重置冷却参数,导致布局减速并最终稳定下来。除非布局一直冷却,否则不应该对tick()
函数进行任何额外调用(尽管这不会成为问题)。
您最后提到的行会产生将变量建立为(可能为空)选择的效果。这意味着您可以将它们与.data()
等D3方法一起使用,以将新数据绑定到它们(实际上这在示例中使用)。只是声明变量不会做同样的事情,因为它没有分配D3选择。在这种情况下,选择为空的事实并不重要。