數據綁定(data join)做爲d3的一大特色,極大方便了對數據及元素的操做
哪裏方便呢?這裏列舉一個簡單的例子:
假設需求以下:有數據[5, 15, 25, 35],製做一張柱狀圖,同時添加交互,鼠標移至對應柱子時變色css
若是咱們有數據綁定,咱們將數據綁定到元素,返回selection以後,有如交互、動畫、更新等的操做直接經過selection進行便可
若是沒有數據綁定,單純的使用js與div元素生成柱狀圖,在交互添加,監聽更新方面都得使用for循環來添加等等,不但開發效率低,並且代碼難以維護html
這還僅僅是柱狀圖,若是場景更加複雜呢?好比地圖的交互,數據更加多維等等
git
本文將經過實驗講解d3的數據綁定是什麼樣的,瞭解數據綁定的三種狀態update、exist、enter
小實驗也將放在CodePen
之中方便你們修改測試github
d3經過data join(數據綁定)創造、更新及銷燬元素,如何操做元素也能經過selection,個人總結以下:
編程
其中,selection的三種狀態將data與element結合在一塊兒,對元素進行控制
data、element、與三種狀態之間的關係咱們來看看data join的概念圖,圖自d3做者Mike Bostock的文章Thinking with Joins
數組
接下來咱們用小實驗進一步進行說明bash
咱們主要會用到如下API,這裏根據官方原文檔結合上圖解釋,更多細節請看官方文檔app
Binds the specified array of data with the selected elements, returning a new selection that represents the update selection: the elements successfullypost
selection.data():綁定數據,返回表明
update的selection
,此selection表示成功綁定了數據的元素,update狀態按上圖理解表示又有數據又有元素;同時定義enter selection和exit selection
,能夠用於添加或刪除元素以匹配數量與數據一致
selection.enter():可在selection.data()返回
的selection中獲取enter selection
selection.exit():可在selection.data()返回
的selection中獲取exit selection
selection.join():添加、移除、重排元素至元素數量與綁定data的數量一致測試
<!-- div元素插入在app之下,修改樣式做爲柱圖 -->
<div id="app">
<!-- 此處根據不一樣實驗肯定不一樣的柱圖(div)數量 -->
</div>
複製代碼
#app div{
font: 10px sans-serif;
background-color: steelblue;
text-align: right;
padding: 3px;
margin: 1px;
color: white;
height:10px;
}
複製代碼
//演示數據
const dataArray = [150, 250, 350];
複製代碼
接下來是幾個小實驗,對應說明不一樣API的功能
接下來開始咱們的實驗✨
<!-- 柱狀圖元素插入在app之下 -->
<div id="app"></div>
複製代碼
第一個例子咱們在控制檯下詳細看看selection.data()
的返回值,在後面的幾個例子中也加以比對 先看例子效果以下:
CodePen打開
我也在CodePen中關鍵的地方console.log()
了,可是是第一個例子咱們就來逐行解釋代碼吧:
首先,選擇#app元素,返回了selection對象
const app = d3.select("#app");
複製代碼
const allSelection = app.selectAll("div") //欲選擇#app下的div元素(即便本來#app下無div元素)
.data(dataArray) //進行數據綁定
console.log("————包含update、enter、exit三個狀態的selection————");
console.log(allSelection);
複製代碼
咱們看看對應的控制檯,我用紅色的箭頭標出三種狀態:
關於group與selection的關係可查看本人以前的總結或者Mike Bostock本人的文章),簡單地說,selection是一個各元素爲group的數組,每個group又是一個DOM數組。
接下來也會有對應的console打印selection.enter()及selection.exit()的代碼,可在控制檯查看
緊些着咱們獲取enter狀態的selection
const enterSelection = allSelection.enter();//獲取enter狀態的selection
console.log("————enter狀態的selection————");
console.log(enterSelection);
複製代碼
const divs = enterSelection.append("div")//在selection中插入"div"
console.log("——EnterNode被插入了div——");
console.log(divs);
//對每一個div設置寬度
divs.style("width", function (d) {
return d + "px";
});
複製代碼
<!-- 柱狀圖元素插入在app之下 -->
<h1>app下子元素數量少於dataArray.length</h1>
<div id="app">
<div></div>
</div>
複製代碼
CodePen打開
其實道理等同於#app下無子元素,都是沒有足夠的元素匹配數據
可是本來存在的div能到數據,即有一個元素是update狀態
首先是進行數據綁定
const dataArray = [150, 250, 350];
const selection = d3.select('#app')
.selectAll('div')
.data(dataArray);
console.log('——————匹配到數據的元素——————')
console.log(selection);
//對匹配到的元素進行樣式設置
selection.style('width', function (d) {
return d + 'px';
});
複製代碼
發現selection中匹配了一個div元素
const enterSelection = selection.enter();
console.log('——————未匹配到數據,取得enter狀態selection——————')
console.log(enterSelection);
console.log('——————插入div,設置樣式——————')
enterSelection.append('div')
.style('width', function (d) {
return d + 'px';
});
複製代碼
最後效果圖與上文同樣
<!-- 柱狀圖元素插入在app之下 -->
<div id="app">
<div></div>
<div></div>
<div></div>
</div>
複製代碼
CodePen打開
此次咱們的app元素下對應剛恰好對應三個數據,元素數量與數據是匹配的。
const dataArray = [150, 250, 350];
const selection = d3.select('#app')
.selectAll('div')
.data(dataArray);
console.log('——————匹配到數據的元素——————')
console.log(selection);
selection.style('width', function (d) {
return d + 'px';
});
複製代碼
咱們能夠在data()返回的selection看到中匹配了三個div,正是對應update狀態
<div id="app">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
複製代碼
因爲設置了高度,width默認爲auto,因此初始下會佔據整個屏幕
const dataArray = [150, 250, 350];
const selection = d3.select('#app')
.selectAll('div')
.data(dataArray);
console.log('——————匹配到數據的元素——————')
console.log(selection);
selection.style('width', function (d) {
return d + 'px';
});
複製代碼
最後一個佔滿屏幕寬度的元素便是exit狀態元素
const exitSelection = selection.exit();
exitSelection.remove();
複製代碼
最後完成如上文的最終圖
以前不管是對enter,exit以及update的操做可能都須要經過selection.enter()及selection.exit()等API獲取selection,使用selection.join()能夠極大地簡化操做,同時局部渲染提升了效率。
這裏咱們舉兩個簡單的例子。
<div id="app"></div>
複製代碼
<div id="app">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
複製代碼
const dataArray = [150, 250, 350];
const selection = d3.select('#app')
.selectAll('div')
.data(dataArray).join('div')
.style('width', function (d) {
return d + 'px';
})
複製代碼
以上僅僅只是靜態數據,但咱們能夠擴展到動態的數據,如data數組元素增長或減小,三種狀態使得咱們便於操做數據,僅僅只需使用selection.join()或者selection.remove()等等
當數據大小改變,或數據量增多減小時,不須要使用if或者for等語法。update,enter及exit三種狀態結合API使得語法簡練,大幅度提高編程效率
其實意義同第一條很相像,三種狀態能夠方便咱們對進入圖表或退出圖表的元素建立動畫,例子以下
<div id="app"></div>
複製代碼
const data = [150, 250, 350];
const t = d3.transition()//定義動畫變換
.duration(500)
.ease(d3.easeLinear);
let selection = d3.select('.chart')
.selectAll('div')
.data(data).join('div').style('width', 0).
transition(t) //使用動畫變換
.style('width', d => d + 'px')
複製代碼
好久之前剛開始學d3的時候一直不理解如此大費周章的意義,直到真正接觸各種圖表後才感慨此設計的妙處所在。
以上如有不足之處,還請指教交流