通过绑定新数据操作元素 [英] Manipulate elements by binding new data

查看:255
本文介绍了通过绑定新数据操作元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图解决如何更新一些D3.js元素只是通过绑定新的数据。我实际上不确定这是否可能,但它感觉像它应该是。



所以首先我创建了四个SVG圈子,并设置 cx offset作为数据的函数:

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

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

//创建初始数据并显示
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圆元素的数组,这是我所期望的。

解决方案

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



你可以通过以下模式解决这个问题:

  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()和whatnot将适用于他们。这可让您使用相同的代码来设置初始值和更新值。



由于您不想在每次更新时重新创建SVG包装,因此您应该创建重画函数之外。



如果要执行转换:

  function redraw(someArray){
var items = foo.selectAll('bar')。
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} 4,c:'red',x:340}
];
myItems.data(data,function(d){return d.id;});

你可以看到一个例子,并在我的D3.js游乐场



首先,看看当您注释掉其中一条数据行时会发生什么,然后再重新打开。接下来,从第4行调用 data()中删除​​参数ƒ('id')

编辑:或者,正如杰出的mbostock所评论的,您可以使用选择.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 code>调用函数并将选择作为参数传递,以便您可以在多个位置对所选内容执行相同的设置。


I am trying to work out how to update some D3.js elements just by binding new data. I'm not actually sure if this is possible or not, but it feels like it should be.

So first I have created four SVG circles, and set the cx offset as a function of the data:

 <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");

Next I attach new data and a transition. I would expect to see the circles move slowly across to the new position (that's what I'm trying to achieve), but they don't:

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

Am I making a basic error? Perhaps I have the wrong selection. Or is it simply not possible to update elements solely by manipulating data?

UPDATE: if I do console.log(circle) then I see an array of SVG circle elements, which is what I would expect.

解决方案

Powerfully (but annoyingly) D3.js sometimes forces you to repeat yourself for simple visualizations. If you tell it that you want to create an element with attributes derived from the data in a certain way, and then later you want to transition those attributes to new data, you must tell it again how to derive the values (in case you wanted to do something different, such as a different visual layout). As @Andrew says, you must tell it what to do when it transitions.

You can work around this 'problem' by following this pattern:

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 });
}

In 2.0 when you append() new items in enter() they are automatically added to the original data-bound selection, so the calls to attr() and whatnot later on will apply to them. This lets you use the same code for both setting initial values and updating values.

Since you don't want to re-create the SVG wrapper each update, you should create this outside the redraw function.

If you want to perform transitions:

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 });
}

Note that the above associates objects with data by index. If you want deleting an intermediary data point to fade out that data point before removing it (instead of removing the last item and transforming every other item to look like that one) then you should specify a unique (non-index) string value to associate each item with:

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; });

You can see an example of this and play with it live on my D3.js playground.

First, see what happens when you comment out one of the data lines, and then put it back again. Next, remove the parameter ƒ('id') from the call to data() on line 4 and again try commenting out and in data lines.

Edit: Alternatively, as commented by the illustrious mbostock, you can use selection.call() along with a reusable function as a way to DRY up your code:

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 });
}

As shown above, .call() invokes a function and passes along the selection as an argument, so that you can perform the same setup on your selection in multiple locations.

这篇关于通过绑定新数据操作元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆