本篇blog的使用的d3版本爲d3.js v5.9.2
將按照簡要介紹、實驗及意義進行
簡要
d3.js通過data join(數據綁定)創建,更新及銷燬元素,如何操作元素則是通過selection。總結如下
其中,selection的三種狀態就將data與elements結合在一起,進行對元素的控制
他們之前的關係如圖所示(圖片來源:Thinking with Joins)
接下來用實驗進一步說明區別吧
實驗
主要會用到以下幾個API:
selection.data():返回代表update
的selection,同時定義enter selection
和exit selection
,update按上圖理解表示又有數據又有元素
selection.enter():返回enter selection
,enter中文爲“進入”,我理解爲有數據但無元素,可以進入圖表
selection.exit():返回exit selection
,exit中文爲“退出”,我理解爲無數據綁定的元素,可以退出圖表
selection.join():對已綁定數據的元素做插入,移除多餘,更新數據,可以簡化操作
初始HTML及CSS
HTML如下
<div class="chart"></div>
CSS如下
.chart div{
font: 10px sans-serif;
background-color: steelblue;
text-align: right;
padding: 3px;
margin: 1px;
color: white;
}
接下來以四種情況熟悉enter,update與exit:
- chart下無子元素
- chart下子元素少於數據(其實和上一種差不多,但爲了方便觀察列出來)
- chart下子元素數量等於數據
- chart下子元素數量多於數據
chart下無子元素
<div class="chart"></div>
數據及JS代碼如下
const data = [10,55,33];
let selection = d3.select('.chart')
.selectAll('div')
.data(data);
console.log(selection);
(注意這裏每一個selection是一個數組對象,每一個元素爲一個數組)
_groups:這裏是代表update的selection,既有數據,又有元素;無元素的數據則用empty表示
enter:有數據,無元素
exit:無數據,有元素
對於enter,可通過selection.enter()進行操作
let enterSelection = selection.enter();
console.log(enterSelection);
enterSelection.append('div')
.style('width', d => d * 10 + 'px')
.text(d => d);
chart下子元素少於數據
<div class="chart">
<div></div>
</div>
const data = [10,55,33];
let selection = d3.select('.chart')
.selectAll('div')
.data(data);
// .style('width', d => d * 10 + 'px') //註釋去掉就會設置第一個div的width
// .text(d => d);
console.log(selection);
let enterSelection = selection.enter();
console.log(enterSelection);
enterSelection.append('div')
.style('width', d => d * 10 + 'px')
.text(d => d);
console.log(selection)
顯示如下:
enter:.chart下有一個div,且這個div有數據綁定,故enter的第一個元素用empty表示,三個數據剩下兩個用EnterNode表示
exit:.chart下有一個div,但他有數據綁定,所以exit中這個div用一個empty表示
_groups(這裏表示update selection):.chart下的有一個div並且綁定上了數據,剩餘兩個數據沒有元素綁定,故用empty表示
chart下子元素數量等於數據
<div class="chart">
<div></div>
<div></div>
<div></div>
</div>
const data = [10,55,33];
let selection = d3.select('.chart')
.selectAll('div')
.data(data);
// .style('width', d => d * 10 + 'px')
// .text(d => d);
console.log(selection);
道理同上
chart下子元素數量多於數據
<div class="chart">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
const data = [10,55,33];
let selection = d3.select('.chart')
.selectAll('div')
.data(data);
// .style('width', d => d * 10 + 'px')
// .text(d => d);
console.log(selection);
可見exit下多了最後一個未綁定數據的元素,即對應圖片中的最後一個元素
可通過selection.exit()對其進行操作
let exitSelection = selection.exit();
console.log(exitSelection);
exitSelection.remove();
selection.join()簡化操作
之前無論是對enter,exit以及update的操作可能都需要通過selection.enter()及selection.exit()等API獲取selection,使用selection.join()可以極大地簡化操作,同時局部渲染提高了效率
以下根據之前的例子簡單舉例
子元素少於數據或無子元素
<div class="chart">
<div></div>
</div>
let selection = d3.select('.chart')
.selectAll('div')
.data(data).join('div')
.style('width', d => d * 10 + 'px')
.text(d => d);
子元素多於數據
<div class="chart">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
let selection = d3.select('.chart')
.selectAll('div')
.data(data).join('div')
.style('width', d => d * 10 + 'px')
.text(d => d);
js是同樣的代碼
,同時把多餘的元素刪去了
data join意義
1.有利於動態數據的可視化編程
以上僅僅只是靜態數據,但我們可以擴展到動態的數據,如data數組元素增加或減少,三種狀態使得我們便於操作數據,僅僅只需使用selection.join()或者selection.remove()等等
2.編程更偏向聲明式
當數據大小改變,或數據量增多減少時,不需要使用if或者for等語法。update,enter及exit三種狀態結合API使得語法簡練,大幅度提升編程效率
3.方便添加動畫效果
其實意義同第一條很相像,三種狀態可以方便我們對進入圖表或退出圖表的元素創建動畫,例子如下
<div class="chart"></div>
const data = [10,55,33];
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 * 10 + 'px')
.text(d => d);
這樣就會有動畫效果了
總結
解決了一直好奇的問題,初步入門,有不對或建議請大佬指正
參考資料
Thinking with Joins
編程範式:命令式編程(Imperative)、聲明式編程(Declarative)和函數式編程(Functional)
selection.data()
selection.enter()
selection.exit()
selection.join()