通过绑定新数据来操纵元素

时间:2012-03-01 14:42:45

标签: javascript d3.js

我试图找出如何通过绑定新数据来更新一些D3.js元素。我不确定这是否可能,但感觉应该是这样。

首先,我创建了四个SVG圈,并将cx偏移设置为数据的函数:

 <body><div id="container"></div></body>

 var svg = d3.select("div.container").append("svg")
     .attr("class", "chart")
     .attr("width", 1000)
     .attr("height", 500);

  // Create initial data and display
  var data = [0, 10, 20, 30];
  var circle = svg.selectAll("circle")
      .data(data)
      .enter()
      .append('circle')
      .attr("cx", function(d) {
        return d*10;
      })
      .attr("cy", 100)
      .attr("r", 10)
      .style("fill", "steelblue");

接下来,我附加新数据和转换。我希望看到圈子慢慢移动到新位置(这就是我想要达到的目标),但他们没有:

  var data1 = [40, 50, 60, 70];
  circle.data(data1).transition().duration(2500);

我是否犯了一个基本错误?也许我选错了。或者仅仅通过操纵数据来更新元素是不可能的?

更新:如果我console.log(circle),那么我会看到一系列SVG圈元素,这正是我所期望的。

3 个答案:

答案 0 :(得分:23)

强大(但令人讨厌)D3.js有时会强迫你重复自己的简单可视化。如果你告诉它你想要以某种方式创建一个具有从数据派生的属性的元素,然后你想要将这些属性转换为新数据,你必须再次告诉它如何派生这些值(如果你想要的话)做一些不同的事情,比如不同的视觉布局。正如@Andrew所说,你必须告诉它在转换时该怎么做。

您可以按照以下模式解决此“问题”:

var foo = d3.select('foo');
function redraw(someArray){
  var items = foo.selectAll('bar').data(someArray);
  items.enter().append('bar');
  items.exit().remove();
  items
    .attr('foo',function(d){ return d });
}

在2.0 append() enter()中的新项目时,它们会自动添加到原始数据绑定选择中,因此对attr()以及之后的内容的调用将适用于它们。这使您可以使用相同的代码来设置初始值和更新值。

由于您不希望每次更新都重新创建SVG包装器,因此您应该在redraw函数之外创建它。

如果要执行转换:

function redraw(someArray){
  var items = foo.selectAll('bar').data(someArray);
  items.enter().append('bar')
    .attr('opacity',0)
    .attr('foo',initialPreAnimationValue);
  items.exit().transition().duration(500)
    .attr('opacity',0)
    .remove();
  items.transition.duration(500)
    .attr('opacity',1)
    .attr('foo',function(d){ return d });
}

请注意,上面通过索引将对象与数据相关联。如果要删除中间数据点以在删除之前淡出该数据点(而不是删除最后一项并将每个其他项转换为看起来那样),那么您应该指定一个唯一的(非索引)字符串值来关联每个项目:

var data = [
  {id:1, c:'red',   x:150},
  {id:3, c:'#3cf',  x:127},
  {id:2, c:'green', x:240},
  {id:4, c:'red',   x:340}
];
myItems.data(data,function(d){ return d.id; });

您可以看到此示例并在my D3.js playground上实时播放。

首先,看看当您注释掉其中一条数据线时会发生什么,然后再将其重新放回。接下来,从第4行的ƒ('id')调用中删除参数data(),然后再次尝试在数据行中注释。

编辑:或者,正如杰出的mbostock所评论的那样,您可以使用selection.call()以及可重复使用的功能来干扰您的代码:

var foo = d3.select('foo');
function redraw(someArray){
  var items = foo.selectAll('bar').data(someArray);
  items.enter().append('bar').call(setEmAll);
  items.exit().remove();
  items.call(setEmAll);
}

function setEmAll(myItems){
  myItems
    .attr('foo',function(d){ return d*2 })
    .attr('bar',function(d){ return Math.sqrt(d)+17 });
}

如上所示,.call()调用一个函数并将选择作为参数传递,以便您可以在多个位置对选择执行相同的设置。

答案 1 :(得分:2)

好的,现在我知道了!您需要告诉它转换的内容:

circle.data(data1).transition().duration(2500).attr("cx", function(d) {
      return d*10;
  });

答案 2 :(得分:2)

用于向现有可视化添加新数据的非常好的资源是关于更新数据的官方教程。

http://bl.ocks.org/mbostock/3808221

主要的一点是,您希望在调用数据时定义一个关键功能,以便d3可以跟踪哪些数据是新的,哪些数据是旧数据。