【发布时间】:2016-07-17 19:10:47
【问题描述】:
我有一个强制布局,我正在向其中动态添加节点。
TL;DR:我的图表的数据绑定似乎在节点和链接之间变得不同步。是否有任何示例使用使用对象引用而不是 ID 或名称来设置动态添加和减少节点的力图?
我见过的所有示例都使用 ID 来映射节点和链接,但文档说(强调):
注意:源属性和目标属性的值可能是初始值 指定为节点数组的索引;这些将被替换为 调用开始后的引用。
在我的例子中,我有一个 Node 类和一个包含节点和链接的 Link 类。链接具有对节点的实际引用,因此我假设我不必再做任何事情来关联节点和链接。
部队网络正在布局网络。但似乎给定 link.source 和 link.target 的节点不是应该存在的节点,这让我认为我对 d3 绑定的假设是不正确的。我是否未能在强制机制中设置(或重置)某些全局状态?
我的图表更新代码是这样的:
GraphView.prototype.updateGraph = function(graph) {
var graph_view = this;
// restart the force layout
this.force
.nodes(graph.nodes)
.links(graph.links);
// update the links
this.link_selection = this.link_selection.data(graph.links);
this.link_selection.exit().remove();
this.link_selection.enter()
.append("svg:path")
.attr("class", "link");
// update the nodes
this.node_selection = this.node_selection.data(graph.nodes);
this.node_selection.exit().remove();
var node_enter = this.node_selection.enter()
.append("g")
.attr("class", "node")
.on('mouseover', this._showTooltip.bind(this))
.on('mouseout', this._hideTooltip.bind(this))
.on('click', this._selectNode.bind(this))
.on('mousedown', this._handleMouseDown.bind(this))
.on("contextmenu", function(data, index) { graph_view._showContextMenu(data, index); })
.call(this.force.drag);
// create outer circle
node_enter
.append("circle")
.attr("class", "annulus")
.style('fill', function(d) { return graph_view.nodeColor(d); })
.style('r', function(d) { return graph_view.nodeRadius(d); })
.attr("x", 0)
.attr("y", 0);
// start ticking...
this.force.start();
};
打勾方法:
GraphView.prototype._tick = function(e) {
this.link_selection
.attr("d", function(d) {
var dx = d.target.x - d.source.x;
var dy = d.target.y - d.source.y;
var dr = Math.sqrt(dx * dx + dy * dy);
return "M " + d.source.x + " " + d.source.y +
" A " + dr + " " + dr + " 0 0 1 "
+ d.target.x + " " + d.target.y;
});
this.node_selection
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')' });
};
【问题讨论】:
-
我担心当你使用
this.link_selection.exit().remove()时这条线this.link_selection.data(graph.links);是不正确的,应该有一种方法来唯一地标识一个带有DOM 的数据。应该是这个this.link_selection.data(graph.links, function(d){return UNIQUE_ID_FOR_DATA}); -
@Cyril:谢谢。但是对象引用本身不是唯一的吗?如果强制布局缓存状态,我可以看到一块被 GC 处理然后重用于另一个节点的内存会导致麻烦,但我正在 #updateGraph() 的第 5 行重新建立链接/节点关联。还是我错过了一些基本的东西?
-
是的,引用是唯一的,但是它将如何比较两个 JSON 对象......它如何知道在更新时要删除哪些节点。这就是您可能必须提供唯一字符串进行比较的原因,以便 d3 知道存在的数据集和您提供的新数据集。并基于该 exit() 选择将给出需要删除的 dom
-
你有没有看到这个问题:"Adding new nodes to Force-directed layout"?
-
在你的例子bl.ocks.org/mbostock/1095795你会发现这个 link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; }); 他为链接数据提供唯一 id 的地方...这是您在代码中缺少的内容,假设对象引用可以解决问题,但我知道它会不是:)
标签: javascript d3.js graph javascript-objects