d3.js數據綁定及示例詳解

前言

數據綁定(data join)做爲d3的一大特色,極大方便了對數據及元素的操做

哪裏方便呢?這裏列舉一個簡單的例子:
假設需求以下:有數據[5, 15, 25, 35],製做一張柱狀圖,同時添加交互,鼠標移至對應柱子時變色css

若是咱們有數據綁定,咱們將數據綁定到元素,返回selection以後,有如交互、動畫、更新等的操做直接經過selection進行便可

若是沒有數據綁定,單純的使用js與div元素生成柱狀圖,在交互添加,監聽更新方面都得使用for循環來添加等等,不但開發效率低,並且代碼難以維護html

這還僅僅是柱狀圖,若是場景更加複雜呢?好比地圖的交互,數據更加多維等等

git

本文將經過實驗講解d3的數據綁定是什麼樣的,瞭解數據綁定的三種狀態updateexistenter
小實驗也將放在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的數量一致測試


以上是我的根據文檔的粗略翻譯,更官方細緻的解釋推薦看官方文檔 解釋錯誤或不到位還請大佬指出
接下來咱們以幾個實驗來講明這些API在數據綁定上的做用 先列出基本的html結構、css及示例數據
<!-- 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下無子元素:可經過enter()添加元素至數量與dataArray.length一致
  • #app下子元素數量少於dataArray.length:可經過enter()添加元素至數量與dataArray.length一致
  • #app下子元素數量等於dataArray.length:selection.data()返回的selection直接操控
  • #app下子元素數量多於dataArray.length:可經過exit()操控多餘元素
  • join簡化操做

接下來開始咱們的實驗✨

#app下無子元素

<!-- 柱狀圖元素插入在app之下 -->
<div id="app"></div>
複製代碼

第一個例子咱們在控制檯下詳細看看selection.data()的返回值,在後面的幾個例子中也加以比對 先看例子效果以下:

CodePen打開
我也在CodePen中關鍵的地方console.log()了,可是是第一個例子咱們就來逐行解釋代碼吧:

首先,選擇#app元素,返回了selection對象

const app = d3.select("#app");
複製代碼

接着,咱們想選擇#app下的div元素進行數據綁定,即便這時初始的#app下沒有子元素
const allSelection = app.selectAll("div") //欲選擇#app下的div元素(即便本來#app下無div元素)
                        .data(dataArray) //進行數據綁定
console.log("————包含update、enter、exit三個狀態的selection————");
console.log(allSelection);
複製代碼

咱們看看對應的控制檯,我用紅色的箭頭標出三種狀態:


其中_groups是對應selection.data()所返回表明update狀態元素,同時定義的enter狀態元素對應_enter,exit狀態元素對應_exit
selection.enter()、selection.exit()也是將返回的_groups做爲各自對應的狀態元素。

關於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);
複製代碼



最後咱們在EnterNode中插入div,根據綁定的數據設置樣式(這裏是寬度)

const divs = enterSelection.append("div")//在selection中插入"div"
    console.log("——EnterNode被插入了div——");
    console.log(divs);
    
    //對每一個div設置寬度
    divs.style("width", function (d) {
       return d + "px";
    });
複製代碼


最終繪製出圖形

#app下子元素數量少於dataArray.length

<!-- 柱狀圖元素插入在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元素


代碼中對他進行了樣式設置,你們能夠去掉那部分代碼看看會發生什麼

接着獲取enterNode,插入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下子元素數量等於dataArray.length

<!-- 柱狀圖元素插入在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狀態


接着對應上面代碼進行樣式設置便可

#app下子元素數量多於dataArray.length

<div id="app">
            <div></div>
            <div></div>
            <div></div>
            <div></div>
        </div>
複製代碼

因爲設置了高度,width默認爲auto,因此初始下會佔據整個屏幕


咱們先對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';
});
複製代碼

最後一個佔滿屏幕寬度的元素便是exit狀態元素


咱們獲取exit元素並將他去除:

const exitSelection = selection.exit();
exitSelection.remove();
複製代碼

最後完成如上文的最終圖

join簡化操做

以前不管是對enter,exit以及update的操做可能都須要經過selection.enter()及selection.exit()等API獲取selection,使用selection.join()能夠極大地簡化操做,同時局部渲染提升了效率。
這裏咱們舉兩個簡單的例子。

子元素少於數據項或無子元素

<div id="app"></div>
複製代碼

CodePen打開

子元素多於數據項

<div id="app">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>
複製代碼

CodePen打開


以上兩個例子的js代碼都是同樣的
const dataArray = [150, 250, 350];

const selection = d3.select('#app')
    .selectAll('div')
    .data(dataArray).join('div')
    .style('width', function (d) {
        return d + 'px';
    })
複製代碼

data join思想的意義

1.有利於動態數據的可視化編程

以上僅僅只是靜態數據,但咱們能夠擴展到動態的數據,如data數組元素增長或減小,三種狀態使得咱們便於操做數據,僅僅只需使用selection.join()或者selection.remove()等等

2.編程更偏向聲明式

當數據大小改變,或數據量增多減小時,不須要使用if或者for等語法。update,enter及exit三種狀態結合API使得語法簡練,大幅度提高編程效率

3.方便添加動畫效果

其實意義同第一條很相像,三種狀態能夠方便咱們對進入圖表或退出圖表的元素建立動畫,例子以下

<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的時候一直不理解如此大費周章的意義,直到真正接觸各種圖表後才感慨此設計的妙處所在。

以上如有不足之處,還請指教交流

參考資料

Thinking with Joins
d3文檔——Joining Data

相關文章
相關標籤/搜索