2012-03-01 18 views
17

我正試圖解決如何通過綁定新數據來更新一些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圈元素的數組,這是我所期望的。

回答

23

期大力(但煩人)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()來電而且以後會適用於他們。這使您可以使用相同的代碼來設置初始值和更新值。

既然你不想重新創建每個更新的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()調用函數並將選擇作爲參數傳遞,以便您可以在多個位置上對選擇執行相同的設置。

+0

真棒回答,謝謝。我仍然試圖讓我的頭腦圍繞這些東西,但這將是一個真正的幫助! – Richard 2012-03-01 18:19:49

+2

周到的答案。你也可以用與[selection.call](https://github.com/mbostock/d3/wiki/Selections#wiki-call)兼容的方式編寫'redraw'函數。相關:[邁向可重用圖表](http://bost.ocks.org/mike/chart/)。 – mbostock 2012-03-06 02:53:38

+1

@mbostock哦,這非常有幫助;我以前沒見過'.call()'。我只是一名D3.js新手,幫助我在哪裏。我將編輯以包含該信息(並且全面通過文檔以嘗試將所有可能性放在我的頭上)。 – Phrogz 2012-03-06 03:22:13

2

好吧,現在我明白了!你需要告訴它怎麼轉型:

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

向現有可視化添加新數據的很好資源是關於更新數據的官方教程。

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

主要外賣的是,你要當你調用數據定義按鍵功能,使D3能跟蹤哪些數據是新的,VS的數據是舊的。

相關問題