D3.js is a JavaScript library for manipulating documents based on data.html
就像我以前文章提到的,D3js 給本身的定位並非圖表,如官網所言,他是數據驅動dom。能理解這一點,就能將之靈活運用到各自場景。好比,給普通table的<td/>
加上數據背景色變成數據透視表;給文本font-size綁定數據,變成簡易詞雲;或者你就是要畫一些數據指標,等等。在這些操做中,首先要用到的就是將dom與數據關聯起來,並對dom進行增刪改。那麼enter 與 exit 兩個函數就是起到這個做用。(若是在react或者vue中,你能夠理解爲dom的diff,只是在d3中咱們是顯式地直接操做dom)vue
code depend d3 version: v5 github.com/d3/d3-selec…react
首先咱們先理解一下概念:假設集合 collectionA,集合 collectionB,判斷兩者之間是否是有交集 equalBy 。git
這張圖,初學d3的同窗都見過。可能解釋的比較少的是中間的equalBy部分。collectionA 即指上一次繪製所棒定的數據,若是上次未綁定數據即[undefine, undefine ....],若是沒有圖形就是空數組[]。collectionB 是咱們要刷新視圖的新數據集合。
如今咱們來看代碼,這是官網的demo以下:程序員
const circle = svg.selectAll("circle").data(data) // UPDATE
.style("fill", "blue");
circle.exit().remove(); // EXIT
circle = circle.enter().append("circle") // ENTER
.style("fill", "green")
.merge(circle) // ENTER + UPDATE
.style("stroke", "black");
複製代碼
其實這裏有個默認選項 即上圖提到的equalBy。 上面的代碼咱們把默認的equalBy補齊以下:github
const equalBy = (d, i) => i; // 根據索引判斷元素是否爲同一個元素
const circle = svg.selectAll("circle").data(data, equalBy) // UPDATE
.style("fill", "blue");
circle.exit().remove(); // EXIT
circle = circle.enter().append("circle") // ENTER
.style("fill", "green")
.merge(circle) // ENTER + UPDATE
.style("stroke", "black");
複製代碼
因此,在沒有指定equalBy的時候,是根據索引判斷元素是否爲同一個元素。數組
const collectionA = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 3, text: 3 }];
const collectionB = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 4, text: 4 }, { id: 5, text: 5 }];
複製代碼
_但咱們正常更新數據時候,equal(collectionA[2],collectionB[2]) == false ,但你未設置equalBy的時候,即默認index爲標記,這裏就認爲是同一個元素 ,equal(collectionA[2],collectionB[2]) == true(就好像一個程序員,10歲的和30歲的他,他的身份證號沒有變,只是頭髮可能由於寫代碼剩的不太多,他的特徵屬性發生了變化),因此它屬於update部分。
機智的你確定能夠想到,那麼若是我給數據集每一個對象一個身份證。bash
const equalBy = obj => obj.id;
const circle = svg.selectAll("circle").data(data, equalBy) // UPDATE
.style("fill", "blue");
複製代碼
這時候對比屬於enter exit 仍是update 則是根據 obj id是否是仍是舊的那個。(也就是,判斷昨天的你和今天的你是不是一我的,是跟據你的身份證id來判斷)。
咱們來個實踐:app
// 僞代碼
const equalBy = (d, i) => i; // 爲設置,即d3默認規則 selection.data(data)
const collectionA = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 3, text: 3 }];
const collectionB = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 4, text: 4 }, { id: 5, text: 5 }];
// enter() = [{ id: 5, text: 5 }]
// exit() = []
// update = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 4, text: 4 }]
const equalBy = obj => obj.id; // 自定義設置規則 selection.data(data, equalBy)
const collectionA = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 3, text: 3 }];
const collectionB = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 4, text: 4 }, { id: 5, text: 5 }];
// enter() = [{ id: 4, text: 4 }, { id: 5, text: 5 }]
// exit() divsext: 3 }]
// update = [{ id: 1, text: 1 }, { id: 2, text: 2 x}]
複製代碼
enter exit update 其實就是對dom元素的增刪該,這很容易讓咱們聯想到react或者vue。這裏我以react爲例來講說。
首先,equalBy 至關於react組件key (reactjs.org/docs/lists-… 即標識這個組件的身份id。一般對於exit咱們會作刪除操做,enter與update 作render component操做(假設咱們的需求僅管理dom,複雜操做本文暫不提,後續獨立篇幅)。dom
那麼 exit 在react中咱們並不須要作什麼,數據不存在,那麼天然就被銷燬。上訴的兩種寫法至關於以下代碼:
class Demo extens React.Component {
render() {
const { data } =this.props;
return (<div> {data.map((obj,i) => <div ref={c => (c.__data__ = obj)} key={i}>obj.text</div>)} </div>)
}
}
class Demo extens React.Component {
render() {
const { data } =this.props;
return (<div> {data.map((obj,i) => <div ref={c => (c.__data__ = obj)} key={obj.id}>obj.text</div>)} </div>)
}
}
複製代碼
細心的你可能發現了一行代碼ref={c => (c.__data__ = obj)}
咱們用react組件建立的元素,若是後續須要使用d3-selection 繼續作一系列操做,好比能夠在componensDidUpdate後執行d3-transition動畫等等,咱們要作的就是將數據關聯到dom中去。 實際上,d3js 就是經過dom的propties __data__
來關聯數據的(注:dom的propties與attribute 的區別)。因此咱們能夠在ref 中獲取dom實例,並賦值掛載。
d3js: d3js.org/
demo工具:beta.observablehq.com/
demo地址: beta.observablehq.com/@leannechn/…
react keys: reactjs.org/docs/lists-…