【问题标题】:Updating a layout.pack in d3.js在 d3.js 中更新 layout.pack
【发布时间】:2013-01-25 11:48:06
【问题描述】:

我正在努力思考 d3 的包布局 (http://bl.ocks.org/4063530)。

我的基本布局工作正常,但我想用新数据更新它。即收集新数据,将其绑定到当前 layout.pack 并相应更新(更新/退出/进入)。

我的尝试在这里(http://jsfiddle.net/emepyc/n4xk8/14/):

var bPack = function(vis) {
    var pack = d3.layout.pack()
    .size([400,400])
    .value(function(d) {return d.time});

    var node = vis.data([data])
    .selectAll("g.node")
    .data(pack.nodes)
    .enter()
    .append("g")
    .attr("class", function(d) { return d.children ? "node" : "leaf node"; })
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

node.append("circle")
    .attr("r", function(d) { return d.r });

    node.filter(function(d) { return !d.children; }).append("text")
    .attr("text-anchor", "middle")
    .attr("dy", ".3em")
    .text(function(d) { return d.analysis_id });

    bPack.update = function(new_data) {
        console.log("UPDATE");

        node
        .data([new_data])
        .selectAll("g.node")
        .data(pack.nodes);

        node
        .transition()
        .duration(1000)
        .attr("class", function(d) { return d.children ? "node" : "leaf node" })
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" });

        node.selectAll("circle")
        .data(new_data)
        .transition()
    .duration(1000)
    .attr("r", function(d) { return d.r; });

    };

具体问题...

如何绑定数据? (因为数据不是复杂的结构,也不是数据数组)

如何将新节点/叶子添加到布局中?旧的被删除了?

非常感谢您提供指向工作示例的指针。

【问题讨论】:

  • 我知道你问这个问题已经有一段时间了,但你能看看我的解决方案吗?我很好奇它是否是您困境的正确答案。也许其他人对您的问题如何解决感兴趣,如果它解决了...... :)
  • 是的,答案是让包布局为您完成所有工作。当我开始研究此问题时,这并不明显,但我发现解决方案在另一个示例中有效(更新树布局)。感谢您的工作示例。
  • 很高兴您设法解决了这个问题。通常情况下,最终的解决方案很简单,但在您第一次解决问题时并不明显。我非常喜欢这些案例。

标签: javascript data-binding d3.js transition circle-pack


【解决方案1】:

工作示例是here

基本上,有用于初始加载的代码,其中所有圆圈、工具提示等都被创建并定位在初始位置。同样,布局(包)也被创建。

然后,每次按下按钮时,都会将新数据加载到包中,并重新计算包。关键代码在这里:

在这里,您现在将数据绑定(加载)到包布局中:(在我的示例中,它是随机数据,当然您将获得来自 json 或代码或类似的数据):

pack.value(function(d) { return 1 +
             Math.floor(Math.random()*501); });

这里计算出新的布局:

pack.nodes(data);

之后,元素会被转换到新的位置,并且它的属性会随着你的决定而改变。

我只是想强调一下,我不使用进入/更新/退出模式或转换(您可能会在其他解决方案中看到),因为我认为这会为此类示例引入不必要的复杂性。

这里有一些正在转换的图片:

开始:

过渡:

结束:

【讨论】:

  • 我正在努力解决您在警告中提到的关于不使用 enter() / exit() 方法的复杂性。我试图让它发挥作用,并想知道你是否可以解释一下增加的复杂性的细微差别。非常感谢您的示例!
  • 你知道这在 D3 v4 中是否可行吗?我没有看到相同的功能。
  • @gwg 不幸的是我不知道。也许我会尝试一些东西,但老实说,我现在和不久的将来都没有时间。我知道圆形包 D3 v3 与 D3 v4 之间存在差异。根据墨菲定律,这在 D3 v4 中是行不通的,也是不可能的,而且在未来的任何 D3 版本中都不可能;)。但如果我发现相关且有用的信息,我会通知您。
【解决方案2】:

我最近遇到了同样的问题,也遇到了通用更新模式教程。这些没有达到我的目的。我在一个图表(ForceLayout)中有几百个 DOM 元素,我正在接收带有每个单独节点属性的 REST 数据。正如您在回应 mg1075 的建议时所说的那样,通过重新绑定数据进行刷新会导致整个图形的重建。在我的例子中,完成更新 DOM 需要几分钟。

我最终为以后需要更新的元素分配了唯一的 ID,然后我用 JQuery 挑选了它们。我的整个图形设置使用 D3,但我的更新没有。这感觉很糟糕,但它工作得很好。与其花费几分钟来销毁和重新创建我的大部分 DOM,不如花费大约 3 秒(留出 REST 调用的时间)。我看不出在 D3 中无法进行属性更新之类的事情的原因。

也许如果 Mike Bostock 为 enter() 选择添加了 remove() 或适当的子选择函数,我们可以遵循纯 D3 模式进行更新。在弄清楚这一点时,我试图绑定数据子集,添加了新属性的数据,然后进行子选择以获取需要更新的元素,但由于输入的有限和特定性质,它不起作用() 选择。

【讨论】:

  • 我认为 Mike 应该只添加一种透明、简单的方法来在圆形包装图中的不同数据之间进行转换。我有一个图表,我需要向下钻取(双击一个圆圈“下潜”),我一次只能从服务器获得 1 个级别,所以每次双击我都会从服务器请求该级别的数据和我想对它进行很好的动画下钻。
【解决方案3】:

相关,如果您尚未查看:
http://bl.ocks.org/3808218 - 通用更新模式,I
http://bl.ocks.org/3808221 - 通用更新模式,II
http://bl.ocks.org/3808234 - 通用更新模式,III

这个示例小提琴没有转换,但这里至少有一种更新数据的方法。

http://jsfiddle.net/jmKH6/

//  VISUALIZATION
var svg = d3.select("#kk")
    .append("svg")
    .attr("width", 500)
    .attr("height", 600)
    .attr("class", "pack"); 

var g = svg.append("g")
    .attr("transform", "translate(2,2)");

var pack = d3.layout.pack()
        .size([400,400])
        .value(function(d) {return d.time});

function update(data) {

    var nodeStringLenth = d3.selectAll("g.node").toString().length; 
    if ( nodeStringLenth > 0) {
        d3.selectAll("g.node")
            .remove();
    }

    var node = g.data([data]).selectAll("g.node")
            .data(pack.nodes);

        node.enter()
          .append("g")
            .attr("class", function(d) { return d.children ? "node" : "leaf node"; })
            .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

        node.append("circle")
            .attr("r", function(d) { return d.r });

        node.filter(function(d) { return !d.children; }).append("text")
            .attr("text-anchor", "middle")
            .attr("dy", ".3em")
            .text(function(d) { return d.analysis_id });

       node
            .exit()
            .remove();
}


var myData = [data1, data2, data3];
update(data1); 
setInterval(function() {
    update( myData[Math.floor(Math.random() * myData.length)] );  // http://stackoverflow.com/questions/4550505/getting-random-value-from-an-array?lq=1
}, 1500);

【讨论】:

  • 感谢您的评论。您在这里所做的是删除所有节点并在每次更新时构建新节点,而不是我想要的。我有兴趣绑定数据并使用转换(或者我应该只使用新值/节点“就地”更改节点而不绑定?)。
猜你喜欢
  • 2017-10-08
  • 2012-07-27
  • 2012-03-27
  • 2022-01-20
  • 2017-04-21
  • 2015-02-20
  • 2017-03-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多