【问题标题】:D3 force layout binding by reference getting out of syncD3通过引用强制布局绑定不同步
【发布时间】: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


【解决方案1】:

我想我明白发生了什么。

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

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

我希望有人提供更完整的答案,以便我可以打勾。但是,如果他们不这样做,我将尝试提供对正在发生的事情的更完整的描述。这是一个小魔法,但不是完全魔法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-15
    • 2013-06-10
    • 2014-05-17
    • 2013-12-02
    • 2014-07-09
    • 2014-04-29
    • 2015-06-25
    • 2013-07-19
    相关资源
    最近更新 更多