如何更新整个数据集而不破坏现有的进入/退出绑定?

时间:2015-01-13 17:06:07

标签: javascript d3.js

修改

我创建了一个更简单的示例,几乎完全来自Mike Bostock的Sankey图示例。您可以看到JSFiddle演示同样的事情。这只是加载一个Sankey可视化,然后定期重新加载完全相同的数据。

这使用我认为的一般更新模式,因为这只处理具有entered的节点/边缘,我不希望发生任何事情。实际上,SVG一遍又一遍地分层。您可以看到更新代码非常简单 - 重新加载原始数据源,这意味着没有新输入的数据条目:

// Initial Load
d3.json("https://dl.dropboxusercontent.com/u/41796243/JSFiddle/D3%20Update/energy.json", function(energy) {
     update(energy);
});

// Refresh
setInterval(function() {
    d3.json("https://dl.dropboxusercontent.com/u/41796243/JSFiddle/D3%20Update/energy.json", function(energy) {
       update(energy);
    })
}, 2000); 

在更新函数中,应简单地为任何新数据条目渲染边缘:

 var link = svg.append("g").selectAll(".link")
      .data(data.links)
    .enter().append("path")
      .attr("class", "link")
      .attr("d", path)
      .style("stroke-width", function(d) { return Math.max(1, d.dy); })
      .sort(function(a, b) { return b.dy - a.dy; });

当渲染时,您可以从下面的屏幕截图中看到这是如何错误地更新边缘的:

enter image description here


原始

我帮助制​​作了一个可视化文件,我正在尝试运行更新。我见过的很多例子只是对对象数据进行内存更新 - 而我想回到服务器并请求替换数据集以查看是否已引入新项目/值已更改。此可视化如下所示:

enter image description here

我最初使用d3.json调用加载数据,如:

d3.json("https://dl.dropboxusercontent.com/u/41796243/JSFiddle/Sankey/data.json", 
        function(data) {
            sankey.data(data);
            sankey.render();
        }
);

我当时想要做的是加载一个新的JSON文件(包含几乎相同的数据 - 只需稍作修改)并更新可视化:

$("#update").click(function() {
    d3.json("https://dl.dropboxusercontent.com/u/41796243/JSFiddle/Sankey/data2.json",
    function(data) {
        sankey.data(data);
        sankey.render();
    })
})

但是,数据似乎失去了所有绑定。如果我一直点击更新按钮,那么您可以看到半透明线条不断叠加在一起,产生如下图片:

enter image description here

现在虽然我没有明确删除链接,只是使用以下代码添加新链接,但我并不期望在图表本身未发生变化时添加任何链接:

// Create new links between nodes
    var links = vis.append("g")
                    .selectAll(".link")
                    .data(data.links);

    links.enter()
        .append("path")
        .attr("class", "link")
        .attr("d", sankey.link())
        .style("stroke-width", "6px")
        .style("marker-end", "url(#end-arrow)")
        .style("stroke", "#fff")
        .style("opacity", 0.2)
        .style("fill", "none")
        .style("hover-opacity", 0.5)
        .sort(function (a, b) { return b.dy - a.dy; });

我期望的是线条保持原样,并且没有新链接,而只是更新现有链接。由于我没有明确更新现有链接,我不希望它们改变外观。

所以问题是为什么会发生这种情况(我的猜测是由于数据的完全重新加载)以及我如何防止它,同时仍然再次加载整个数据集?

这是展示问题的JSFiddle。有趣的代码在#97-#150行之间的control.render函数中。

1 个答案:

答案 0 :(得分:1)

您的实施中的问题是,在调用g函数时,您经常会添加两个新的update元素。您可以在JSFiddle中观察到这一点(过了一段时间,您的g元素中有相当多的svg

如何解决?嗯,有很多方法,这里有一个例子:

首先添加一些容器来放置元素:

var linkContainer = svg.append('g');
var nodeContainer = svg.append('g');

(您在定义svg后立即执行此操作,即外部更新功能。

接下来,在更新功能中将元素添加到这些容器中:

var link = linkContainer.selectAll(".link")....

同样适用于节点。

我希望这可以解决这个问题。

另一种方法是将svg元素中的组绑定到一些随机固定数据并在其上使用.enter().append()(从而确保它只添加一次,因为数据已修复...)

我希望这有帮助!