聲明:本教程針對D3.js v3版本進行講解。css
D3 的全稱是(Data-Driven Documents),顧名思義能夠知道是一個被數據驅動的文檔。聽名字有點抽象,說簡單一點,其實就是一個 JavaScript 的函數庫,使用它主要是用來作數據可視化的。html
D3 提供了各類簡單易用的函數,大大簡化了 JavaScript 操做數據的難度。因爲它本質上是 JavaScript ,因此用 JavaScript 也是能夠實現全部功能的,但它能大大減少你的工做量,尤爲是在數據可視化方面,D3 已經將生成可視化的複雜步驟精簡到了幾個簡單的函數,你只須要輸入幾個簡單的數據,就可以轉換爲各類絢麗的圖形。有過 JavaScript 基礎的朋友必定很容易理解它。git
咱們知道如今有不少開源的圖表庫Echarts、HighCharts、G2.js等等。那麼D3跟這些圖表庫相比有什麼優點和劣勢?github
D3 基於svg,所以對圖像進行放大不會失真。(D3.js v4版本已經支持canvas了,本系列教程只講v3版本)canvas
D3 相對來講較底層,對初學者來講不太方便,可是一旦掌握了,就比其餘工具更加駕輕就熟。下圖展現了 D3 與其它可視化工具的區別:api
選擇 D3:若是但願開發腦海中任意想象到的圖表。數組
選擇 Highcharts、Echarts 等:若是但願開發幾種固定種類的、十分大衆化的圖表。瀏覽器
看起來,D3 彷佛是爲藝術家或發燒友準備的。有那麼點意思,但請初學者也不要放棄。app
SVG,指可縮放矢量圖形(Scalable Vector Graphics),是用於描述二維矢量圖形的一種圖形格式,是由萬維網聯盟制定的開放標準。SVG 使用 XML 格式來定義圖形,除了 IE8 以前的版本外,絕大部分瀏覽器都支持 SVG,可將 SVG 文本直接嵌入 HTML 中顯示。less
SVG 有以下特色:
想要經過 D3 來開啓數據可視化之旅的朋友,須要什麼預備知識呢?
有人會問:多久能學會D3.js?
我能夠告訴各位:只要跟着我這個D3入門系列教程學習、練習下來,你就學會了用D3.js編寫經常使用的圖表。學得快的人,兩三天就掌握了,學的慢的人,可能須要一週時間吧。
下面開始學習D3.js具體技術細節。。。
在 D3 中,用於選擇元素的函數有兩個:
這兩個函數返回的結果稱爲選擇集。
這裏涉及一個概念:選擇集
使用 d3.select() 或 d3.selectAll() 選擇元素後返回的對象,就是選擇集。
例如,選擇集的常見用法以下:
var body = d3.select("body"); //選擇文檔中的body元素
var p1 = body.select("p"); //選擇body中的第一個p元素
var p = body.selectAll("p"); //選擇body中的全部p元素
var svg = body.select("svg"); //選擇body中的svg元素
var rects = svg.selectAll("rect"); //選擇svg中全部的svg元素
複製代碼
選擇集和綁定數據一般是一塊兒使用的。
D3 有一個很獨特的功能:能將數據綁定到 DOM 上,也就是綁定到文檔上。這麼說可能很差理解,例如網頁中有段落元素 和一個整數 5,因而能夠將整數 5 與 綁定到一塊兒。綁定以後,當須要依靠這個數據才操做元素的時候,會很方便。
D3 中是經過如下兩個函數來綁定數據的:
相對而言,data() 比較經常使用。
假設如今有三個段落元素以下:
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
複製代碼
接下來分別使用 datum() 和 data(),將數據綁定到上面三個段落元素上。
假設有一字符串 China,要將此字符串分別與三個段落元素綁定,代碼以下:
var str = "China";
var body = d3.select("body");
var p = body.selectAll("p");
p.datum(str);
p.text(function(d, i){
return "第 "+ i + " 個元素綁定的數據是 " + d;
});
複製代碼
綁定數據後,使用此數據來修改三個段落元素的內容,其結果以下:
第 0 個元素綁定的數據是 China
第 1 個元素綁定的數據是 China
第 2 個元素綁定的數據是 China
複製代碼
在上面的代碼中,用到了一個無名函數 function(d, i)。 當選擇集須要使用被綁定的數據時,常須要這麼使用。其包含兩個參數,其中:
例如,上述例子中:第 0 個元素 apple 綁定的數據是 China。
有一個數組,接下來要分別將數組的各元素綁定到三個段落元素上。
var dataset = ["I like dogs","I like cats","I like snakes"];
複製代碼
綁定以後,其對應關係的要求爲:
調用 data() 綁定數據,並替換三個段落元素的字符串爲被綁定的字符串,代碼以下:
var body = d3.select("body");
var p = body.selectAll("p");
p.data(dataset)
.text(function(d, i){
return d;
});
複製代碼
這段代碼也用到了一個匿名函數 function(d, i),其對應的狀況以下:
此時,三個段落元素與數組 dataset 的三個字符串是一一對應的,所以,在函數 function(d, i) 直接 return d 便可。
結果天然是三個段落的文字分別變成了數組的三個字符串。
I like dogs
I like cats
I like snakes
複製代碼
前面已經講解了 select 和 selectAll,以及選擇集的概念。本節具體講解這兩個函數的用法。
假設在 body 中有三個段落元素:
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
複製代碼
如今,要分別完成如下四種選擇元素的任務。
使用 select ,參數傳入 p 便可,如此返回的是第一個 p 元素。
var p1 = body.select("p");
p1.style("color","red");
複製代碼
結果以下,被選擇的元素標記爲紅色。
使用 selectAll 選擇 body 中全部的 p 元素。
var p = body.selectAll("p");
p.style("color","red");
複製代碼
結果以下:
有很多方法,一種比較簡單的是給第二個元素添加一個 id 號。
<p id="myid">Pear</p>
複製代碼
而後,使用 select 選擇元素,注意參數中 id 名稱前要加 # 號。
var p2 = body.select("#myid");
p2.style("color","red");
複製代碼
結果以下:
給後兩個元素添加 class,
<p class="myclass">Pear</p>
<p class="myclass">Banana</p>
複製代碼
因爲須要選擇多個元素,要用 selectAll。注意參數,class 名稱前要加一個點。
var p = body.selectAll(".myclass");
p.style("color","red");
複製代碼
結果以下:
關於 select 和 selectAll 的參數,實際上是符合 CSS 選擇器的條件的,即用「井號(#)」表示 id,用「點(.)」表示 class。
此外,對於已經綁定了數據的選擇集,還有一種選擇元素的方法,那就是靈活運用 function(d, i)。咱們已經知道參數 i 是表明索引號的,因而即可以用條件斷定語句來指定執行的元素。
插入元素涉及的函數有兩個:
假設有三個段落元素,與上文相同。
body.append("p")
.text("append p element");
複製代碼
在 body 的末尾添加一個 p 元素,結果爲:
Apple
Pear
Banana
append p element
複製代碼
在 body 中 id 爲 myid 的元素前添加一個段落元素。
body.insert("p","#myid")
.text("insert p element");
複製代碼
已經指定了 Pear 段落的 id 爲 myid,所以結果以下。
Apple
insert p element
Pear
Banana
複製代碼
刪除一個元素時,對於選擇的元素,使用 remove 便可,例如:
var p = body.select("#myid");
p.remove();
複製代碼
如此便可刪除指定 id 的段落元素。
柱形圖是一種最簡單的可視化圖表,主要有矩形、文字標籤、座標軸組成。本節爲簡單起見,只繪製矩形的部分,用以講解如何使用 D3 在 SVG 畫布中繪圖。
D3 雖然沒有明文規定必定要在 SVG 中繪圖,可是 D3 提供了衆多的 SVG 圖形的生成器,它們都是隻支持 SVG 的。所以,建議使用 SVG 畫布。 使用 D3 在 body 元素中添加 svg 的代碼以下:
var width = 300; //畫布的寬度
var height = 300; //畫布的高度
var svg = d3.select("body") //選擇文檔中的body元素
.append("svg") //添加一個svg元素
.attr("width", width) //設定寬度
.attr("height", height); //設定高度
複製代碼
有了畫布,接下來就能夠在畫布上做圖了。
本文繪製一個橫向的柱形圖。只繪製矩形,不繪製文字和座標軸。
在 SVG 中,矩形的元素標籤是 rect。例如:
<svg>
<rect></rect>
<rect></rect>
</svg>
複製代碼
上面的 rect 裏沒有矩形的屬性。矩形的屬性,經常使用的有四個:
要注意,在 SVG 中,x 軸的正方向是水平向右,y 軸的正方向是垂直向下的。
如今給出一組數據,要對此進行可視化。數據以下:
var dataset = [ 250 , 210 , 170 , 130 , 90 ]; //數據(表示矩形的寬度)
複製代碼
爲簡單起見,咱們直接用數值的大小來表示矩形的像素寬度(後面會說到這不是一種好方法)。而後,添加如下代碼。
var rectHeight = 25; //每一個矩形所佔的像素高度(包括空白)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x",20)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return d;
})
.attr("height",rectHeight-2)
.attr("fill","steelblue");
複製代碼
這段代碼添加了與 dataset 數組的長度相同數量的矩形,所使用的語句是:
svg.selectAll("rect") //選擇svg內全部的矩形
.data(dataset) //綁定數組
.enter() //指定選擇集的enter部分
.append("rect") //添加足夠數量的矩形元素
複製代碼
這段代碼之後會經常出如今 D3 的代碼中,請務必牢記。目前不深刻討論它的做用機制是怎樣的,只須要讀者牢記,當:
有數據,而沒有足夠圖形元素的時候,使用此方法能夠添加足夠的元素。
添加了元素以後,就須要分別給各元素的屬性賦值。在這裏用到了 function(d, i),前面已經講過,d 表明與當前元素綁定的數據,i 表明索引號。給屬性賦值的時候,是須要用到被綁定的數據,以及索引號的。
最後一行的:
.attr("fill","steelblue");
複製代碼
是給矩形元素設置顏色。通常來講,最好寫成外置 CSS 的形式,方便歸類和修改。這裏爲了便於初學者理解,將樣式直接寫到元素裏。
結果圖如本文開頭的圖片所示。
代碼示例地址:github.com/legend-li/D…
比例尺是 D3 中很重要的一個概念,上一章裏曾經提到過直接用數值的大小來表明像素不是一種好方法,本章正是要解決此問題。
上一章製做了一個柱形圖,當時有一個數組:
var dataset = [ 250 , 210 , 170 , 130 , 90 ];
複製代碼
繪圖時,直接使用 250 給矩形的寬度賦值,即矩形的寬度就是 250 個像素。
此方式很是具備侷限性,若是數值過大或太小,例如:
var dataset_1 = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
var dataset_2 = [ 2500, 2100, 1700, 1300, 900 ];
複製代碼
對以上兩個數組,毫不可能用 2.5 個像素來表明矩形的寬度,那樣根本看不見;也不可能用 2500 個像素來表明矩形的寬度,由於畫布沒有那麼長。
因而,咱們須要一種計算關係,可以:將某一區域的值映射到另外一區域,其大小關係不變。
這就是比例尺(Scale)。
比例尺,很像數學中的函數。例如,對於一個一元二次函數,有 x 和 y 兩個未知數,當 x 的值肯定時,y 的值也就肯定了。
在數學中,x 的範圍被稱爲定義域,y 的範圍被稱爲值域。
D3 中的比例尺,也有定義域和值域,分別被稱爲 domain 和 range。開發者須要指定 domain 和 range 的範圍,如此便可獲得一個計算關係。
D3 提供了多種比例尺,下面介紹最經常使用的兩種。
線性比例尺,能將一個連續的區間,映射到另外一區間。要解決柱形圖寬度的問題,就須要線性比例尺。
假設有如下數組:
var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];
複製代碼
現有要求以下:
將 dataset 中最小的值,映射成 0;將最大的值,映射成 300。
代碼以下:
var min = d3.min(dataset);
var max = d3.max(dataset);
var linear = d3.scale.linear()
.domain([min, max])
.range([0, 300]);
linear(0.9); //返回 0
linear(2.3); //返回 175
linear(3.3); //返回 300
複製代碼
其中,d3.scale.linear() 返回一個線性比例尺。domain() 和 range() 分別設定比例尺的定義域和值域。在這裏還用到了兩個函數,它們常常與比例尺一塊兒出現:
這兩個函數可以求數組的最大值和最小值,是 D3 提供的。按照以上代碼,
比例尺的定義域 domain 爲:[0.9, 3.3]
比例尺的值域 range 爲:[0, 300]
所以,當輸入 0.9 時,返回 0;當輸入 3.3 時,返回 300。當輸入 2.3 時呢?返回 175,這是按照線性函數的規則計算的。
有一點請你們記住:
d3.scale.linear() 的返回值,是能夠當作函數來使用的。所以,纔有這樣的用法:linear(0.9)。
有時候,定義域和值域不必定是連續的。例如,有兩個數組:
var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];
複製代碼
咱們但願 0 對應顏色 red,1 對應 blue,依次類推。
可是,這些值都是離散的,線性比例尺不適合,須要用到序數比例尺。
var ordinal = d3.scale.ordinal()
.domain(index)
.range(color);
ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black
複製代碼
用法與線性比例尺是相似的。
在上一節的基礎上,修改一下數組,再定義一個線性比例尺。
var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
var linear = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, 250]);
複製代碼
其後,按照上一章的方法添加矩形,在給矩形設置寬度的時候,應用比例尺。
var rectHeight = 25; //每一個矩形所佔的像素高度(包括空白)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x",20)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return linear(d); //在這裏用比例尺
})
.attr("height",rectHeight-2)
.attr("fill","steelblue");
複製代碼
如此一來,全部的數值,都按照同一個線性比例尺的關係來計算寬度,所以數值之間的大小關係不變。
座標軸,是可視化圖表中常常出現的一種圖形,由一些列線段和刻度組成。座標軸在 SVG 中是沒有現成的圖形元素的,須要用其餘的元素組合構成。D3 提供了座標軸的組件,如此在 SVG 畫布中繪製座標軸變得像添加一個普通元素同樣簡單。
在 SVG 畫布的預約義元素裏,有六種基本圖形:
另外,還有一種比較特殊,也是功能最強的元素:
畫布中的全部圖形,都是由以上七種元素組成。
顯然,這裏面沒有座標軸 這種元素。若是有的話,咱們能夠採用相似如下的方式定義:
<axis x1="" x2="" ...></axis>
複製代碼
很惋惜,沒有這種元素。可是,這種設計是合理的:不可能爲每一種圖形都配備一個單獨的元素,那樣 SVG 就會過於龐大。
所以,咱們須要用其餘元素來組合成座標軸,最終使其變爲相似如下的形式:
<g>
<!-- 第一個刻度 -->
<g>
<line></line> <!-- 第一個刻度的直線 -->
<text></text> <!-- 第一個刻度的文字 -->
</g>
<!-- 第二個刻度 -->
<g>
<line></line> <!-- 第二個刻度的直線 -->
<text></text> <!-- 第二個刻度的文字 -->
</g>
...
<!-- 座標軸的軸線 -->
<path></path>
</g>
複製代碼
分組元素
若是須要手動添加這些元素就太麻煩了,爲此,D3 提供了一個組件:d3.svg.axis()。它爲咱們完成了以上工做。
上面提到了比例尺的概念,要生成座標軸,須要用到比例尺,它們兩者常常是一塊兒使用的。下面,在上一章的數據和比例尺的基礎上,添加一個座標軸的組件。
//數據
var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
//定義比例尺
var linear = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, 250]);
var axis = d3.svg.axis()
.scale(linear) //指定比例尺
.orient("bottom") //指定刻度的方向
.ticks(7); //指定刻度的數量
複製代碼
第 1 – 2 行:定義數組。
第 4 – 7 行:定義比例尺,其中使用了數組 dataset。
第 9 – 12 行:定義座標軸,其中使用了線性比例尺 linear。其中:
定義了座標軸以後,只須要在 SVG 中添加一個分組元素
svg.append("g")
.call(axis);
複製代碼
上面有一個 call() 函數,其參數是前面定義的座標軸 axis。
在 D3 中,call() 的參數是一個函數。調用以後,將當前的選擇集做爲參數傳遞給此函數。也就是說,如下兩段代碼是相等的。
function foo(selection) {
selection
.attr("name1", "value1")
.attr("name2", "value2");
}
foo(d3.selectAll("div"))
複製代碼
和
d3.selectAll("div").call(foo);
複製代碼
所以,
svg.append("g").call(axis);
複製代碼
與
axis(svg.append(g));
複製代碼
是相等的。
默認的座標軸樣式不太美觀,下面提供一個常見的樣式:
<style> .axis path, .axis line{ fill: none; stroke: black; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 11px; } </style>
複製代碼
分別定義了類 axis 下的 path、line、text 元素的樣式。接下來,只須要將座標軸的類設定爲 axis 便可。
座標軸的位置,能夠經過 transform 屬性來設定。
一般在添加元素的時候就一併設定,寫成以下形式:
svg.append("g")
.attr("class","axis")
.attr("transform","translate(20,130)")
.call(axis);
複製代碼
代碼示例地址:github.com/legend-li/D…
一個完整的柱形圖包含三部分:矩形、文字、座標軸。本章將對前幾章的內容進行綜合的運用,製做一個實用的柱形圖,內容包括:選擇集、數據綁定、比例尺、座標軸等內容。
//畫布大小
var width = 400;
var height = 400;
//在 body 裏添加一個 SVG 畫布
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//畫布周邊的空白
var padding = {left:30, right:30, top:20, bottom:20};
複製代碼
上面定義了一個 padding,是爲了給 SVG 的周邊留一個空白,最好不要將圖形繪製到邊界上。
//定義一個數組
var dataset = [10, 20, 30, 40, 33, 24, 12, 5];
//x軸的比例尺
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, width - padding.left - padding.right]);
//y軸的比例尺
var yScale = d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([height - padding.top - padding.bottom, 0]);
複製代碼
x 軸使用序數比例尺,y 軸使用線性比例尺。要注意兩個比例尺值域的範圍。
d3.range()用法講解:
d3.range([start,]stop[,step]); //返回等差數列
var a = d3.range( 10);
console.log(a) //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
var b = d3.range( 2,10 );
console.log(b);//[2, 3, 4, 5, 6, 7, 8, 9]
var c = d3.range( 2,10 ,2);
console.log(c);//[2, 4, 6, 8]
複製代碼
ordinal.rangeRoundBands()用法講解: github.com/d3/d3/wiki/…
//定義x軸
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
//定義y軸
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
複製代碼
x 軸刻度的方向向下,y 軸的向左。
//矩形之間的空白
var rectPadding = 4;
//添加矩形元素
var rects = svg.selectAll(".MyRect")
.data(dataset)
.enter()
.append("rect")
.attr("class","MyRect")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return yScale(d);
})
.attr("width", xScale.rangeBand() - rectPadding )
.attr("height", function(d){
return height - padding.top - padding.bottom - yScale(d);
});
//添加文字元素
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return yScale(d);
})
.attr("dx",function(){
return (xScale.rangeBand() - rectPadding)/2;
})
.attr("dy",function(d){
return 20;
})
.text(function(d){
return d;
});
複製代碼
矩形元素和文字元素的 x 和 y 座標要特別注意,要結合比例尺給予適當的值。
//添加x軸
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
//添加y軸
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
複製代碼
座標軸的位置要結合空白 padding 的值來設定。
代碼示例地址:github.com/legend-li/D…
下一章:《【D3.js 入門系列二】理解 Update && Enter && Exit、製做交互式動態圖表》
參考資料:www.ourd3js.com/
D3.js(v3)中文api:github.com/d3/d3/wiki/…