官方網站:http://d3js.org/
github:https://github. com/mbostock/d3/tags
forks最新:https://github.com/mbostock/d3
d3圖片匯總:https://github.com/mbostock/d3/wiki/Gallery
全面學習網站:http://www.ourd3js.com
網絡統一配置
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
1. 如何選擇元素
在 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元素
2. 如何綁定數據
D3 有一個很獨特的功能:能將數據綁定到 DOM 上,也就是綁定到文檔上。這么說可能不好理解,例如網頁中有段落元素 <p> 和一個整數 5,於是可以將整數 5 與 <p>綁定到一起。綁定之后,當需要依靠這個數據才操作元素的時候,會很方便。
D3 中是通過以下兩個函數來綁定數據的:
datum():綁定一個數據到選擇集上
data():綁定一個數組到選擇集上,數組的各項值分別與選擇集的各元素綁定
datum()
假設有一字符串 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)。當選擇集需要使用被綁定的數據時,常需要這么使用。其包含兩個參數,其中:
d 代表數據,也就是與某元素綁定的數據。
i 代表索引,代表數據的索引號,從 0 開始。
data()
有一個數組,接下來要分別將數組的各元素綁定到三個段落元素上。
var dataset = ["I like dogs","I like cats","I like snakes"];
綁定之后,其對應關系的要求為:
Apple 與 I like dogs 綁定
Pear 與 I like cats 綁定
Banana 與 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),其對應的情況如下
當 i == 0 時, d 為 I like dogs。 當 i == 1 時, d 為 I like cats。 當 i == 2 時, d 為 I like snakes。
此時,三個段落元素與數組 dataset 的三個字符串是一一對應的,因此,在函數 function(d, i) 直接 return d 即可。
結果自然是三個段落的文字分別變成了數組的三個字符串。
I like dogs
I like cats
I like snakes
選擇第一個 p 元素
<p>Apple</p> <p>Pear</p> <p>Banana</p>
var p1 = body.select("p"); p1.style("color","red");
結果如下圖,被選擇的元素標記為紅色。

選擇全部 p 元素
var p = body.selectAll("p"); p.style("color","red");

選擇第二個 p 元素
<p id="myid">Pear</p>
然后,使用 select 選擇元素,注意參數中 id 名稱前要加 # 號。
var p2 = body.select("#myid"); p2.style("color","red");

選擇后兩個 p 元素
<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 是代表索引號的,於是便可以用條件判定語句來指定執行的元素。
3. 插入元素
append():在選擇集末尾插入元素
insert():在選擇集前面插入元素
假設有三個段落元素,與上文相同。
append()
body.append("p")
.text("append p element");
在 body 的末尾添加一個 p 元素,結果為:
Apple
Pear
Banana
append p element
insert()
在 body 中 id 為 myid 的元素前添加一個段落元素。
body.insert("p","#myid")
.text("insert p element");
已經指定了 Pear 段落的 id 為 myid,因此結果如下。
Apple
insert p element
Pear
Banana
4. 刪除元素
刪除一個元素時,對於選擇的元素,使用 remove 。
var p = body.select("#myid"); p.remove();
圖標制作
一,柱形圖

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元素為d3自動添加,不需要在頁面標出)
<svg> <rect></rect> <rect></rect> </svg>
面的 rect 里沒有矩形的屬性。矩形的屬性,常用的有四個:
x:矩形左上角的 x 坐標
y:矩形左上角的 y 坐標
width:矩形的寬度
height:矩形的高度
要注意,在 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 的形式,方便歸類和修改。這里為了便於初學者理解,將樣式直接寫到元素里。
比例尺的使用
1. 線性比例尺
線性比例尺,能將一個連續的區間,映射到另一區間
var min = d3.min(dataset); var max = d3.max(dataset); var linear = d3.scale.linear() .domain([min, max]) .range([0, 300]);//映色范圍:映射成 0;將最大的值,映射成 300。 linear(0.9); //返回 0 linear(2.3); //返回 175 linear(3.3); //返回 300
其中,d3.scale.linear() 返回一個線性比例尺。domain() 和 range() 分別設定比例尺的定義域和值域。在這里還用到了兩個函數,它們經常與比例尺一起出現:
d3.max()
d3.min()
這兩個函數能夠求數組的最大值和最小值,是 D3 提供的。按照以上代碼,
比例尺的定義域 domain 為:[0.9, 3.3]
比例尺的值域 range 為:[0, 300]
因此,當輸入 0.9 時,返回 0;當輸入 3.3 時,返回 300。當輸入 2.3 時呢?返回 175,這是按照線性函數的規則計算的。
d3.scale.linear() 的返回值,是可以當做函數來使用的。因此,才有這樣的用法:linear(0.9)。
2. 序數比例尺
有時候,定義域和值域不一定是連續的。例如,有兩個數組:
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
用法與線性比例尺是類似的
給柱形圖添加比例尺
對上面柱形圖做如下修改修改:
<head> <meta charset="utf-8"> <title>HelloWorld</title> </head> <body> </body> </html> <script src="d3.js" charset="utf-8"></script> <script> var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ]; //數據(表示矩形的寬度) var width = 300; //畫布的寬度 var height = 300; //畫布的高度 var svg = d3.select("body") //選擇文檔中的body元素 .append("svg") //添加一個svg元素 .attr("width", width) //設定寬度 .attr("height", height); //設定高度 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"); </script>
5.坐標軸
d3.svg.axis()
分組元素 <g>,是 SVG 畫布中的元素,意思是 group。此元素是將其他元素進行組合的容器,在這里是用於將坐標軸的其他元素分組存放
生成坐標軸,需要用到比例尺,它們二者經常是一起使用的。下面,在上一章的數據和比例尺的基礎上,添加一個坐標軸的組件。
//數據 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。其中:
d3.svg.axis():D3 中坐標軸的組件,能夠在 SVG 中生成組成坐標軸的元素。
scale():指定比例尺。
orient():指定刻度的朝向,bottom 表示在坐標軸的下方顯示。
ticks():指定刻度的數量。
在 SVG 中添加坐標軸
定義了坐標軸之后,只需要在 SVG 中添加一個分組元素 <g>,再將坐標軸的其他元素添加到這個 <g> 里即可。代碼如下:
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);
全列:
<head> <meta charset="utf-8"> <title>HelloWorld</title> <style> .axis path, .axis line{ fill: none; stroke: black; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 11px; } </style> </head> <body> </body> </html> <script src="d3.js" charset="utf-8"></script> <script> var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ]; //數據(表示矩形的寬度) var width = 300; //畫布的寬度 var height = 300; //畫布的高度 var svg = d3.select("body") //選擇文檔中的body元素 .append("svg") //添加一個svg元素 .attr("width", width) //設定寬度 .attr("height", height); //設定高度 var linear = d3.scale.linear() .domain([0, d3.max(dataset)]) .range([0, 250]); var axis = d3.svg.axis() .scale(linear) //指定比例尺 .orient("bottom") //指定刻度的方向 .ticks(7); //指定刻度的數量 svg.append("g") .attr("class","axis") .attr("transform","translate(20,130)") .call(axis); 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"); </script>
一個完整的矩形圖
<html> <head> <meta charset="utf-8"> <title>完整的柱形圖</title> </head> <style> .axis path, .axis line{ fill: none; stroke: black; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 11px; } .MyRect { fill: steelblue; } .MyText { fill: white; text-anchor: middle; } </style> <body> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> <script> //畫布大小 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}; //定義一個數組 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軸 var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); //定義y軸 var yAxis = d3.svg.axis() .scale(yScale) .orient("left"); //矩形之間的空白 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軸 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); </script> </body> </html>
6.動態效果
D3 支持制作動態的圖表。有時候,圖表的變化需要緩慢的發生,以便於讓用戶看清楚變化的過程,也能給用戶不小的友好感。
實現動態的方法
transition()
啟動過渡效果。
其前后是圖形變化前后的狀態(形狀、位置、顏色等等),例如:
.attr("fill","red") //初始顏色為紅色
.transition() //啟動過渡
.attr("fill","steelblue") //終止顏色為鐵藍色
D3 會自動對兩種顏色(紅色和鐵藍色)之間的顏色值(RGB值)進行插值計算,得到過渡用的顏色值。我們無需知道中間是怎么計算的,只需要享受結果即可。
duration()
指定過渡的持續時間,單位為毫秒。
如 duration(2000) ,指持續 2000 毫秒,即 2 秒。
ease()
指定過渡的方式,常用的有:
linear:普通的線性變化
circle:慢慢地到達變換的最終狀態
elastic:帶有彈跳的到達最終狀態
bounce:在最終狀態處彈跳幾次
調用時,格式形如:
ease(“bounce”)。
delay()
指定延遲的時間,表示一定時間后才開始轉變,單位同樣為毫秒。此函數可以對整體指定延遲,也可以對個別指定延遲。
例如,對整體指定時:
.transition()
.duration(1000)
.delay(500)
如此,圖形整體在延遲 500 毫秒后發生變化,變化的時長為 1000 毫秒。因此,過渡的總時長為1500毫秒。
又如,對一個一個的圖形(圖形上綁定了數據)進行指定時:
.transition()
.duration(1000)
.delay(funtion(d,i){
return 200*i;
})
如此,假設有 10 個元素,那么第 1 個元素延遲 0 毫秒(因為 i = 0),第 2 個元素延遲 200 毫秒,第 3 個延遲 400 毫秒,依次類推….整個過渡的長度為 200 * 9 + 1000 = 2800 毫秒。
實現簡單的動態效果
下面將在 SVG 畫布里添加三個圓,圓出現之后,立即啟動過渡效果。
第一個圓,要求移動 x 坐標。
var circle1 = svg.append("circle") .attr("cx", 100) .attr("cy", 100) .attr("r", 45) .style("fill","green"); //在1秒(1000毫秒)內將圓心坐標由100變為300 circle1.transition() .duration(1000) .attr("cx", 300);
第二個圓,要求既移動 x 坐標,又改變顏色。
var circle2 = svg.append("circle")... //與第一個圓一樣,省略部分代碼 //在1.5秒(1500毫秒)內將圓心坐標由100變為300, //將顏色從綠色變為紅色 circle2.transition() .duration(1500) .attr("cx", 300) .style("fill","red");
第三個圓,要求既移動 x 坐標,又改變顏色,還改變半徑。
var circle3 = svg.append("circle")... //與第一個圓一樣,省略部分代碼 //在2秒(2000毫秒)內將圓心坐標由100變為300 //將顏色從綠色變為紅色 //將半徑從45變成25 //過渡方式采用bounce(在終點處彈跳幾次) circle3.transition() .duration(2000) .ease("bounce") .attr("cx", 300) .style("fill","red") .attr("r", 25);
全例
<html> <head> <meta charset="utf-8"> <title>讓圖表動起來</title> </head> <body> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> <script> //畫布大小 var width = 400; var height = 400; //在 body 里添加一個 SVG 畫布 var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); var circle1 = svg.append("circle") .attr("cx", 100) .attr("cy", 100) .attr("r", 45) .style("fill","green"); //在1秒(1000毫秒)內將圓心坐標由100變為300 circle1.transition() .duration(1000) .attr("cx", 300); var circle2 = svg.append("circle") .attr("cx", 100) .attr("cy", 200) .attr("r", 45) .style("fill","green"); //在1.5秒(1500毫秒)內將圓心坐標由100變為300, //將顏色從綠色變為紅色 circle2.transition() .duration(1500) .attr("cx", 300) .style("fill","red"); var circle3 = svg.append("circle") .attr("cx", 100) .attr("cy", 300) .attr("r", 45) .style("fill","green"); //在2秒(2000毫秒)內將圓心坐標由100變為300 //將顏色從綠色變為紅色 //將半徑從45變成25 //過渡方式采用bounce(在終點處彈跳幾次) circle3.transition() .duration(2000) .ease("bounce") .attr("cx", 300) .style("fill","red") .attr("r", 25); </script> </body> </html>
給柱形圖加上動態效果
<html> <head> <meta charset="utf-8"> <title>讓圖表動起來</title> <style> .axis path, .axis line{ fill: none; stroke: black; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 11px; } .MyRect { fill: steelblue; } .MyText { fill: white; text-anchor: middle; } </style> </head> <body> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> <script> //畫布大小 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}; //定義一個數組 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軸 var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); //定義y軸 var yAxis = d3.svg.axis() .scale(yScale) .orient("left"); //矩形之間的空白 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("width", xScale.rangeBand() - rectPadding ) .attr("y",function(d){ var min = yScale.domain()[0]; return yScale(min); }) .attr("height", function(d){ return 0; }) .transition() .delay(function(d,i){ return i * 200; }) .duration(2000) .ease("bounce") .attr("y",function(d){ return yScale(d); }) .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("dx",function(){ return (xScale.rangeBand() - rectPadding)/2; }) .attr("dy",function(d){ return 20; }) .text(function(d){ return d; }) .attr("y",function(d){ var min = yScale.domain()[0]; return yScale(min); }) .transition() .delay(function(d,i){ return i * 200; }) .duration(2000) .ease("bounce") .attr("y",function(d){ return yScale(d); }); //添加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); </script> </body> </html>
