Javascript D3通过引用强制布局绑定不同步

Javascript D3通过引用强制布局绑定不同步,javascript,d3.js,graph,javascript-objects,Javascript,D3.js,Graph,Javascript Objects,我有一个动态添加节点的强制布局 TL;DR:我的图形的数据绑定在节点和链接之间似乎不同步。是否有使用对象引用而不是ID或名称动态加减节点来设置力图的示例 我看到的所有示例都使用ID来映射节点和链接,但文档中说(重点添加): 注:源属性和目标属性的值可以是初始值 指定为节点数组中的索引;这些将被替换为 调用start之后的引用 在我的例子中,我有一个节点类和一个包含节点和链接的链接类。链接有对节点的实际引用,因此我假设我不必再做任何事情来关联节点和链接 部队网络正在做它的事情,规划一个网络。但似乎

我有一个动态添加节点的强制布局

TL;DR:我的图形的数据绑定在节点和链接之间似乎不同步。是否有使用对象引用而不是ID或名称动态加减节点来设置力图的示例

我看到的所有示例都使用ID来映射节点和链接,但文档中说(重点添加):

注:源属性和目标属性的值可以是初始值 指定为节点数组中的索引;这些将被替换为 调用start之后的引用

在我的例子中,我有一个节点类和一个包含节点和链接的链接类。链接有对节点的实际引用,因此我假设我不必再做任何事情来关联节点和链接

部队网络正在做它的事情,规划一个网络。但似乎给定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 + ')' });
};

我想我明白发生了什么

首先,我没有看到在d3.layout.force中使用对象引用的任何示例,因为它不起作用:关联链接和节点的机制要求每个链接和节点提供一个不可变的句柄,即id(是的,可以使用数组索引,但这是另一个问题)

稍微令人惊讶的是,不可变句柄可以是任何东西,只要它对其相应的节点或链接是唯一的。事实上,节点id和链接id之间不需要任何关系——它们仅在设置数组索引时使用


我希望有人能提供一个更完整的答案,这样我就可以打勾了。但如果他们没有,我会尝试提供一个更完整的描述发生了什么。这是一个小魔术,但不完全是魔术

我想我明白发生了什么

首先,我没有看到在d3.layout.force中使用对象引用的任何示例,因为它不起作用:关联链接和节点的机制要求每个链接和节点提供一个不可变的句柄,即id(是的,可以使用数组索引,但这是另一个问题)

稍微令人惊讶的是,不可变句柄可以是任何东西,只要它对其相应的节点或链接是唯一的。事实上,节点id和链接id之间不需要任何关系——它们仅在设置数组索引时使用


我希望有人能提供一个更完整的答案,这样我就可以打勾了。但如果他们没有,我会尝试提供一个更完整的描述发生了什么。这是一个小魔术,但不完全是魔术

我担心这行
this.link\u selection.data(graph.links)此.link\u selection.exit().remove()时,code>不正确。
应该有一种方法用DOM唯一标识数据。它应该是this
this.link\u selection.data(graph.links,函数(d){returnunique\u ID\u FOR\u data})@Cyril:谢谢。但是对象引用本身不是唯一的吗?我可以看出,如果强制布局处于缓存状态,那么一块得到GC’d然后重新用于另一个节点的内存将如何导致问题,但我正在#updateGraph()的第5行中重新建立链接/节点关联。还是我遗漏了一些基本的东西?是的,引用是唯一的,但它如何比较两个JSON对象…它如何知道更新时要删除哪些节点。这就是为什么您可能必须给出一个唯一的字符串进行比较,以便d3知道当前的数据集和您提供的新数据集。基于这个exit()选择将给出需要删除的dom您看到这个问题了吗:?在您的示例中,您还会发现这个link=link.data(force.links(),函数(d){return d.source.id+“-”+d.target.id;});他为链接数据提供唯一id的地方…这是您在代码中缺少的,假设对象引用会起作用,但我知道不会:)我担心这行
This.link\u selection.data(graph.links)此.link\u selection.exit().remove()时,code>不正确。
应该有一种方法用DOM唯一标识数据。它应该是this
this.link\u selection.data(graph.links,函数(d){returnunique\u ID\u FOR\u data})@Cyril:谢谢。但是对象引用本身不是唯一的吗?我可以看出,如果强制布局处于缓存状态,那么一块得到GC’d然后重新用于另一个节点的内存将如何导致问题,但我正在#updateGraph()的第5行中重新建立链接/节点关联。还是我遗漏了一些基本的东西?是的,引用是唯一的,但它如何比较两个JSON对象…它如何知道更新时要删除哪些节点。这就是为什么您可能必须给出一个唯一的字符串进行比较,以便d3知道当前的数据集和您提供的新数据集。基于这个exit()选择将给出需要删除的dom您看到这个问题了吗:?在您的示例中,您还会发现这个link=link.data(force.links(),函数(d){return d.source.id+“-”+d.target.id;});他为链接数据提供唯一id的位置…这是您在代码中缺少的,假设对象引用可以实现这一点,但我知道它不会:)