初識 D3.js :打造專屬可視化

1、前言

隨着如今自定義可視化的需求日益增加,Highcharts、echarts等高度封裝的可視化框架已經沒法知足用戶各類強定製性的可視化需求了,這個時候D3的無限定製的能力就脫穎而出。git

若是想要經過D3完成可視化,除了對於D3自己API的學習, 關於web標準的HTML, SVG, CSS, Javascript 和 數據可視化的概念以及標準都是須要學習的。這無疑帶來了較高的學習門檻,但這也是值得的,由於掌握 D3 後,咱們幾乎能夠實現任何 2d 的可視化需求。github

本文經過對D3核心模塊分析以及進行具體案例實踐的方式,來幫助初學者學習瞭解D3的繪圖思路。web

2、D3是什麼

D3的全稱是 Data-Driven Documents(數據驅動文檔),是基於數據來操做文檔的 JavaScript 庫,其核心在於使用繪圖指令對數據進行轉換,在源數據的基礎上建立新的可繪製數據, 生成SVG路徑以及經過數據和方法在DOM中建立數據可視化元素(如軸)。數組

相對於Echats等開箱即用的可視化框架來講,D3更接近底層,它能夠直接控制原生的SVG元素,而且不直接提供任何一種現成的可視化圖表,全部的圖表都需咱們在它的庫裏挑選合適的方法構建而成,這也大大提升了它的可視化定製能力。並且D3 沒有引入新的圖形元素,它遵循了web標準(HTML, CSS, SVG 以及 Canvas )來展現數據 ,因此它能夠不須要依賴其餘框架獨立運行在現代瀏覽器中。瀏覽器

3、D3 核心模塊

在V4版本後,D3的 API 如今已經被拆分紅一個個模塊,咱們能夠根據本身的可視化需求進行按需加載。根據泛義能夠將D3 API模塊分爲如下的幾大類:DOM操做、數據處理,數據分析轉換、地理路徑,行爲等app

 

 

這裏咱們主要對 D3-selection 和 D3-scale 模塊進行解析:echarts

3.1  D3-selection

D3-selection(選擇集) 是 D3js的核心模塊,主要是用來進行選擇元素,設置屬性、數據綁定,事件綁定等操做。框架

選擇元素:D3-selection 提供了兩種方法來獲取目標元素,d3.select():返回目標元素的第一個節點,d3.selectAll():返回目標元素的集合,乍一看有點相似原生API 的 querySelector 和 querySelectorAll,可是 d3.select 返回的是一個 selection 對象,querySelector 返回的是一個 NodeList 數組。經過控制檯打印的信息,能夠看到 selection 下的 groups 存放了全部選擇的元素集合,parents 存放了全部選中元素的父節點。dom

 

設置屬性或者綁定事件:咱們不須要關心 groups 的結構是怎麼樣的。當調用 selection.attr  或者 selection.style 的時候, selection 中的全部 group 的全部子元素都會被調用,group 存在的惟一影響是: 當咱們傳參是一個function 的時候,例如 selection.attr('attrName', function(data, i)) 或 selection.on('click', function(data, i)) 時, 傳遞的 function(data, i) 中, 第二個參數 i 是元素在 group 中的索引而不是在整個 selection 中的索引。函數

數據綁定:其實是給選擇的DOM元素的 __data__ 屬性賦值,這裏提供了3種方式進行數據綁定:

(1)給每個單獨的 DOM 元素調用 selection.datum:d3.select('body').datum(20) 等價於 document.body.__data__ = 20

(2)從父節點中繼承來數據, 好比: append , insert , select,子節點會主動繼承父節點的數據:

 

(3) 調用 selection.data() 方法,支持傳入裝有基礎數據類型的數據,也支持傳入一個function(parentNode, groupIndex)根據節點索引與數據作映射,data()方法引入了 d3 中很是重要的 join 思想: 

綁定 data 到 DOM 元素, 在D3中是經過比較 data 和 DOM 的 key 值來找到對應關係的。 若是咱們沒有單獨設置 key 值,那麼默認根據 data 的下標索引來設定,可是當數據順序發生改變,這個默認下標 key 值 就變得不可靠了,這時咱們可使用 selection.data(data, keyFunction) 中的第二個參數 keyFunction,根據當前的數據返回一個對應的 key 值。經過下面的圖例能夠看出,不論是有一個仍是多個 group(每一個group 都是獨立的),只要咱們保證在任意一個 group 中的 key 值是惟一的,數據一旦發生變化都會反映給對應的 DOM 元素( update 的過程):

 

3.2 Join 思想

上面提到的都是data數據和DOM元素數量相同的狀況下的數據綁定,那若是data數據和DOM元素數量不相同時,咱們來看看 D3 又是如何進行數據綁定的:如今終於能夠來介紹 D3-selecion 模塊的核心 Join 思想了,這個思想簡單來講就是 「不該該告訴D3去怎麼建立元素, 而是告訴D3,.selectAll() 獲得的 selecion 集合應該和  .data(data) 綁定的數據要怎麼一一對應」。  

從上圖能夠看出,在進行 d3.data(data) 數據綁定的時候,會產生三種狀態的選擇集:

  • Update:  已經和data數據綁定的DOM元素集合
  • Enter:data數據沒有找到與之對應的DOM元素集合(就是缺失的DOM元素)
  • Exit: 沒有被數據綁定的DOM元素集合(多餘的DOM元素)

用 Join 的方式來理解意味着,咱們要作的事情僅僅是聲明 DOM集合和數據集合之間的關係, 而且經過處理三個不一樣狀態的集合 enter、update 、 exit 來描述這種關係。這種方式能夠大大簡化咱們對DOM元素的操做,咱們不須要再用 if 和 for 循環的方式來進行復雜的邏輯判斷,來獲得咱們須要獲得的元素集合。而且在處理動態數據的時候,能夠經過處理這三種狀態,輕鬆的展現實時數據和添加平滑的動態交互效果。

3. 3 D3-scale

D3-scale(比列尺) 提供多種不一樣類型的比例尺。常常和 D3-axis 座標軸模塊一塊兒使用。

D3-scale 提供了多種連續性和非連續性的比例尺,整體能夠將他們分爲三大類:

  • 連續性輸入(domain)和連續性輸出(range)
  • 連續性輸入(domain)和離散性輸出(range)
  • 離散性輸入(domain)和離散性輸出(range)

經常使用的一些比例尺:

(1)d3-scaleLinear 線性比例尺(連續性輸入和連續性輸出)

能夠看出,調用d3.scaleLinear()能夠生成線性比例尺,domain()是輸入域,range()是輸出域,至關於將domain中的數據集映射到range的數據集中。

使用示例:

 

映射關係:

 

(2)d3-scaleTime 時間比例尺(連續性輸入和連續性輸出)

時間比例尺與線性比例尺相似,只不過輸入域變成了一個時間軸。正常咱們使用比例尺都是個正序的過程,可是D3也提供了invert()以及invertExtent()方法,咱們能夠經過輸出域中的具體值得出對應輸入域的值。

使用示例:

 

(3)d3.scaleQuantize  量化比例尺(連續性輸入和離散性輸出)

量化比例尺是將連續的輸入域根據輸出域被分割爲均勻的片斷,因此它的輸出域是離散的。

使用示例:

映射關係:

(4)d3. scaleThreshold 閾值比例尺(連續性輸入和離散性輸出)

閾值比例尺能夠爲一組連續數據指定分割閾值,閾值比例尺默認的 domain:[0.5] 以及默認的 range:[0, 1] ,所以默認的 d3.scaleThreshold() 等價於 Math.round 函數。 閾值比例尺輸入域爲 N 的話,輸出域必須爲 N + 1,不然比例尺對某些值可能會返回 undefined,或者輸出域多餘的值會被忽略。

使用示例:

 

存在三種映射關係:

a. 當domain和range的數據是 N : N+1   

b. 當domain和range的數據是 N : N + 大於1         

c. 當domain和range的數據是 N + 大於0 :  N 

(5)d3.scaleOrdinal 序數比例尺(離散性輸入和離散性輸出)

與scaleLinear等連續性比例尺不一樣,序數比例尺的輸出域和輸入域都是離散的。

使用示例:

 

存在三種映射關係:

a.當domain和range的數據是一一對應     

 

b.當domain少於range的數據

c.當domain多於range的數據

 

4、實戰

經過以上的學習,應該對d3是如何操做DOM以及座標軸的數據映射爲相應的可視化表現有了必定的瞭解,下面咱們來實際運用這兩個模塊,來實現咱們常見的可視化圖表:柱狀圖。

(1)首先添加一個SVG元素。

 

(2)根據咱們上面說到 d3.scale 模塊以及 d3.axis 模塊繪製座標軸,d3.scaleBand() 叫作序數分段比例尺,相似咱們說的 d3.scaleOrdinal() 序數比例尺,可是它支持連續的數值類型的輸出域,離散的輸入域能夠將連續的範圍劃分爲均勻的分段。這裏再講一個細節,在繪製網格的時候,咱們並無額外添加 line 元素來實現,而是經過 d3.axis 座標軸模塊的 axis.ticks() 方法對座標軸刻度進行了設置,經過 tickSIze() 設置了刻度線長度,來模擬和圖表寬度相等的網格線,而且還能夠經過 tickFormat() 對Y軸刻度值進行格式化轉換。

 

(3)座標軸繪製好了後,咱們經過數據綁定來繪製與之對應的矩形(rect)元素了。

 

(4)這個時候柱狀圖已經基本繪製好了,咱們再豐富內容展現,添加標籤、標題等提示信息。

 

(5)最後咱們經過給柱子綁定監聽事件,實現tooltips的信息浮層交互。

 

5、總結

經過對 d3.selection 、d3.scale 以及 d3.axis等模塊的學習,咱們已經能夠繪製出經常使用的柱狀圖等圖表,咱們也能夠經過d3提供的其餘模塊繪製出更加複雜的可視化效果,例如經過  d3-hierarchy(層級模塊) 實現層級樹圖可視化,d3-geo(地理投影) 實現地圖數據可視化等,本文講解的內容還只是D3庫的冰山一角。因此等咱們掌握了D3後,限制咱們實現可視化的再也不是技術而是想象力。

做者:Ray

相關文章
相關標籤/搜索