?d3.js官網(wǎng)https://d3js.org/。全稱為“data-driven Documents”,3個(gè)d嘛,簡(jiǎn)稱d3.js。使用的話,官網(wǎng)上面有下載連接,就是一個(gè)zip壓縮包。解壓后把里面的d3.js引入就可以使用了,它并需要使用npm來安裝,就是一個(gè)普通的js文件罷了,就像我們使用jquery,下載下來引入就可以使用了,方法是一樣的。
? ? ??
d3.js是干嘛的呢?
????????大家有用過類似的圖表展現(xiàn)工具吧,比如百度的echarts,國(guó)外的highcharts之類的,d3.js跟這些圖標(biāo)插件很類似,都是用來做數(shù)據(jù)可視化的,常說“一張圖勝過千言萬語”,在如今的大數(shù)據(jù)時(shí)代,數(shù)據(jù)的可視化顯得尤為重要,而d3.js在這方面做得很是突出,d3.js也被稱為操作svg的jquery,因?yàn)閐3.js的圖表都是基于svg的,在操作svg方面的語法與jquery的方法很是類似。
我們來看一個(gè)例子,實(shí)現(xiàn)的功能是使用d3.js在網(wǎng)頁中寫入“hello d3.js”,網(wǎng)頁結(jié)構(gòu)如下:
1 2 3 4 5 6 7 8 9 10 | <!DOCTYPE?html> < html > ???? < head > ???????? < meta ?charset = "utf-8" > ???????? < title >index</ title > ???????? < script ?src = "d3.min.js" ></ script > ???? </ head > ???? < body > ???? </ body >?? </ html > |
使用d3.js的JavaScript代碼:
1 2 3 4 | <script> ???? var ?body=d3.select( "body" ); ???? body.append( "h1" ).text( "hello?d3.js" ); </script> |
打開這個(gè)網(wǎng)頁:

使用d3.js在頁面上畫個(gè)圓圈
?為了熟悉d3.js的api,我們通過一個(gè)小小的案例來領(lǐng)略一下d3.js的風(fēng)采。這個(gè)案例很簡(jiǎn)單,就是使用d3.js在頁面上畫一個(gè)圓形,最終的效果如下圖所示:
????
以下是代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!DOCTYPE?html> < html > ???? < head > ???????? < meta ?charset = "utf-8" > ???????? < title >index</ title > ???????? < script ?src = "d3.min.js" ></ script > ???? </ head > ???? < body > ???? </ body > < script > ???? var?body=d3.select("body"); ???? //body元素尾部添加一個(gè)svg標(biāo)簽 ???? var?svg=body.append("svg").attr("width",300).attr("height",300); ???? //svg尾部添加一個(gè)circle標(biāo)簽并設(shè)置屬性 ???? svg.append("circle").attr("cx",100).attr("cy",100).attr("r",50) ???? .attr("fill","#123456").attr("stroke","red").attr("stroke-width","2px"); </ script > </ html > |
代碼解釋:
???? d3.select用于獲取html文檔中的元素,里面的參數(shù)是css選擇器,返回值是單個(gè)的經(jīng)過d3封裝的html元素。
???? append方法用于在調(diào)用者尾部附加一個(gè)元素,
?????attr(屬性名,屬性值)用于設(shè)置調(diào)用該方法的元素的屬性,具體屬性名是什么,自然跟調(diào)用該方法的元素有關(guān),比如width和height都是svg元素有的屬性,而下面的cx,cy都是svg里面的cricle標(biāo)簽的屬性,可不是我隨便亂寫的。
????學(xué)習(xí)過我前邊寫的svg相關(guān)的教程的同學(xué)都知道,最后一行的fill,stroke這些都是樣式,對(duì)這些樣式的控制我們最好還是寫在css里,然后使用d3.js把這個(gè)css類加上就可以了,因此可以如下修改代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <!DOCTYPE?html> <html> ???? <head> ???????? <meta?charset= "utf-8" > ???????? <title>index</title> ???????? <script?src= "d3.min.js" ></script> ???????? <style> ???????????? .mycircle{ ???????????????? fill: #123456; ???????????????? stroke:red; ???????????????? stroke-width:2px; ???????????? } ???????? </style> ???? </head> ???? <body> ???? </body> <script> ???? var ?body=d3.select( "body" ); ???? //body元素尾部添加一個(gè)svg標(biāo)簽 ???? var ?svg=body.append( "svg" ).attr( "width" ,300).attr( "height" ,300); ???? //svg尾部添加一個(gè)circle標(biāo)簽并設(shè)置屬性 ???? svg.append( "circle" ).attr( "cx" ,100).attr( "cy" ,100).attr( "r" ,50) ???? .attr( "class" , "mycircle" ); </script> </html> |
d3的update對(duì)象、enter對(duì)象、exit對(duì)象
先看下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <!DOCTYPE?html> < html > ???? < head > ???????? < meta ?charset = "utf-8" > ???????? < title >index</ title > ???????? < script ?src = "d3.min.js" ></ script > ???? </ head > ???? < body > ???????? < div >div1</ div > ???????? < div >div2</ div > ???????? < div >div3</ div > ???? </ body > < script > ???? var?divs=d3.selectAll("div"); ???? //divs是經(jīng)過d3包裝的元素?cái)?shù)組 ???? console.log(divs); ???? var?arr=["a","b","c"]; ???? var?update=divs.data(arr); ???? //update對(duì)象是已經(jīng)綁定了數(shù)據(jù)集的選擇集 ???? console.log(update); ???? update.text(function(val,index){ ???????? return?"坐標(biāo):"+index+",值:"+val; ???? }); </ script > </ html > |
執(zhí)行結(jié)果:

selectAll:返回的是經(jīng)過d3包裝的元素?cái)?shù)組,稱為d3的選擇集
data用于把數(shù)組的元素依次綁定到選擇集的元素上,有點(diǎn)類似es6里面解構(gòu)賦值的意思。
datum也是綁定數(shù)據(jù),和data()的區(qū)別在于datum是直接綁定數(shù)據(jù)到選擇集上,并不存在"解構(gòu)",如divs.datum(arr);則每個(gè)div將被綁定a,b,c
上面我說了,update對(duì)象就是綁定了數(shù)據(jù)集的選擇集,在上面的這個(gè)例子中,選擇集的長(zhǎng)度是3,數(shù)據(jù)集的長(zhǎng)度也是3,正好可以一一對(duì)應(yīng),但是現(xiàn)實(shí)的業(yè)務(wù)不可能這么完美,兩者總有不相等的時(shí)候。
一、當(dāng)選擇集divs的大小>數(shù)據(jù)集arr的長(zhǎng)度時(shí)
????????這個(gè)時(shí)候如果要想讓他們一一對(duì)應(yīng),我們應(yīng)該把divs中的多余的元素給找到并刪除,可以這樣理解,在d3.js中找到的多余的選擇集里面的元素就是exit對(duì)象,把數(shù)據(jù)arr改為兩個(gè)長(zhǎng)度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <!DOCTYPE?html> < html > ???? < head > ???????? < meta ?charset = "utf-8" > ???????? < title >index</ title > ???????? < script ?src = "d3.min.js" ></ script > ???? </ head > ???? < body > ???????? < div >div1</ div > ???????? < div >div2</ div > ???????? < div >div3</ div > ???? </ body > < script > ???? var?divs=d3.selectAll("div"); ???? //divs是經(jīng)過d3包裝的元素?cái)?shù)組 ???? console.log(divs); ???? var?arr=["a","b"]; ???? var?update=divs.data(arr); ???? //update對(duì)象是已經(jīng)綁定了數(shù)據(jù)集的選擇集 ???? console.log(update); ???? update.text(function(val,index){ ???????? return?"坐標(biāo):"+index+",值:"+val; ???? }); ???? //此時(shí)有多余的div,找到(exit)并刪除 ???? var??exit=?update.exit(); ???? console.log(exit); ???? exit.remove(); </ script > </ html > |
添加exit.remove();前后的結(jié)果對(duì)比:
二、當(dāng)選擇集divs的大小<數(shù)據(jù)集arr的長(zhǎng)度時(shí)
????????此時(shí)要想對(duì)應(yīng),需要多補(bǔ)幾個(gè)選擇集的元素,補(bǔ)幾個(gè)?補(bǔ)什么元素?都是通過d3.js可以控制的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <!DOCTYPE?html> < html > ???? < head > ???????? < meta ?charset = "utf-8" > ???????? < title >index</ title > ???????? < script ?src = "d3.min.js" ></ script > ???? </ head > ???? < body > ???????? < div >div1</ div > ???????? < div >div2</ div > ???????? < div >div3</ div > ???? </ body > < script > ???? var?divs=d3.selectAll("div"); ???? //divs是經(jīng)過d3包裝的元素?cái)?shù)組 ???? console.log(divs); ???? var?arr=["a","b","c","d","e"]; ???? var?update=divs.data(arr); ???? //update對(duì)象是已經(jīng)綁定了數(shù)據(jù)集的選擇集 ???? console.log(update); ???? update.text(function(val,index){ ???????? return?"坐標(biāo):"+index+",值:"+val; ???? }); ???? ? ???? //增加enter對(duì)象 ???? //enter對(duì)象可以獲取數(shù)據(jù)集多幾個(gè)元素 ???? var??enter=?update.enter(); ???? console.log(enter); ???? //多幾個(gè)就補(bǔ)幾個(gè)p ???? enter.append("p").text(function(val,index){ ???????? return?"坐標(biāo):"+index+",值:"+val; ???? }); </ script > </ html > |
增加enter對(duì)象前后結(jié)果對(duì)比:
 |  ? |
?
使用d3畫一個(gè)沒有刻度的柱形圖
最終的效果如下:

代碼如下,html代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!DOCTYPE?html> < html > ???? < head > ???????? < meta ?charset = "utf-8" > ???????? < title >index</ title > ???????? < script ?src = "d3.min.js" ></ script > ???????? < style > ???????????? .bar{ ???????????????? fill:#123456; ???????????? } ???????? </ style > ???? </ head > ???? < body >? ???? </ body > </ html > |
d3畫柱形圖代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <script> ???? var ?dataset=[20,30,70,10,50]; ???? var ?body=?d3.select( "body" ); ???? var ?svg=body.append( "svg" ).attr( "width" ,500).attr( "height" ,300); ???? var ?bars=svg.selectAll( ".bar" ).data(dataset).enter().append( "rect" ) ???????????? .attr( "class" , "bar" ) ???????????? .attr( "x" , function (val,index){ ???????????????? return ?30*index+50; ???????????? }) ???????????? .attr( "y" , function (val,index){ ???????????????? return ?300-val-50; ???????????? }) ???????????? .attr( "width" ,25) ???????????? .attr( "height" , function (val,index){ ???????????????? return ?val; ???????????? }) </script> |
其他的都好理解,可能你不能理解的地方在于
attr("y",function(val,index){
return 300-val-50;
})
這一段,這一段的作用是讓各個(gè)柱子都處在離svg下邊緣50px的位置,也就是說讓所有柱子都位于一條水平線上,如果不這樣,那就不叫柱形圖了,變成k線圖了....,為什么寫上“300-val-50”就可以位于一條水平線上了呢?看下面的圖:

rect的y坐標(biāo)是從上往下數(shù)的,這樣子就應(yīng)該理解了吧。
柱形圖的完善---引入d3.js中的比例尺
在d3.js中,有很多種比例尺,常用的有兩種。什么叫做比例尺呢?這個(gè)我相信大家心里都有個(gè)內(nèi)化于心的概念,在d3.js中,我說的直白點(diǎn),就是把一組值映射為另一組值,在這個(gè)映射的過程中,關(guān)系保持不變。那么為什么需要比例尺呢?在“使用d3畫一個(gè)沒有刻度的柱形圖”上篇文章里面,設(shè)置柱狀圖的高度,我使用的代碼是
1 2 3 | attr( "height" , function (val,index){ ???????????????? return ?val; ???????????? } |
也就是說,我直接使用了數(shù)據(jù)集里面元素的值作為柱狀圖的高度,因?yàn)閿?shù)據(jù)集是var dataset=[20,30,70,10,50];整個(gè)svg大小我設(shè)置的寬高分別為500px和300px,并沒有看出問題,可是我并不能擔(dān)保在真正使用的時(shí)候數(shù)據(jù)集永遠(yuǎn)不會(huì)超過svg的大小,也就是說數(shù)據(jù)集里面有了個(gè)2000的值怎么辦?就顯示不了了呀,這就是問題的所在。因此需要引入比例尺。
d3.js中的兩種比例尺:
一、線性比例尺
1 2 3 4 5 6 7 8 9 10 | var ?dataset=[20,3000,70,100,2.5,0]; var ?min?=?d3.min(dataset); var ?max?=?d3.max(dataset); var ?linear?=?d3.scaleLinear() ???????? .domain([min,?max]) ???????? .range([0,?250]); ? console.log(linear(0));???? //返回?0 console.log(linear(3000));???? //返回?250 console.log(linear(1500));???? //返回?125 |
我這里使用的是d3的最新版本v5.0.0,在d3-v3的版本中,得到比例尺的寫法是d3.scale.linear(),不管哪種寫法,只是版本的不同罷了,返回的都是比例尺的對(duì)象,這個(gè)對(duì)象是個(gè)函數(shù),因此我們后邊可以直接使用linear(1500)的方式來寫,其實(shí)就是調(diào)用的函數(shù),參數(shù)是原來的數(shù)字,返回值為經(jīng)過比例尺轉(zhuǎn)化后的數(shù)字。domain([最小,最大])指定原來的值范圍,range([最小,最大])指定映射范圍。
二、序數(shù)比例尺
上邊的線性比例尺指定的是范圍、區(qū)間,可以映射這個(gè)區(qū)間內(nèi)任意的值。但是,有時(shí)候我們需要映射的可能是離散的值,比如我想把[1,2,3]映射為["a","b","c"];使用線性比例尺就不行了,而應(yīng)該使用序數(shù)比例尺。
1 2 3 4 5 6 7 8 | var ?dataset=[1,2,3]; ???????? var ?ordinal?=?d3.scaleOrdinal() ???????????????? .domain(dataset) ???????????????? .range([ "a" , "b" , "c" ]); ? ???????? console.log(ordinal(1));???? //返回?a ???????? console.log(ordinal(2));???? //返回?b ???????? console.log(ordinal(3));???? //返回?c |
比例尺介紹完了,我們把上篇文章里面的柱狀圖改善一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <script> ???? var ?dataset=[20,30,70,10,50]; ???? //定義比例尺 ???? var ?min?=?d3.min(dataset); ???? var ?max?=?d3.max(dataset); ???? var ?linear?=?d3.scaleLinear() ???????????? .domain([0,?max])? //注意,這里不要用[min,max],不然最小的柱子高度就是0了,導(dǎo)致看不到 ???????????? .range([0,?250]); ???? ? ???? var ?body=?d3.select( "body" ); ???? var ?svg=body.append( "svg" ).attr( "width" ,500).attr( "height" ,300); ???? var ?bars=svg.selectAll( ".bar" ).data(dataset).enter().append( "rect" ) ???????????? .attr( "class" , "bar" ) ???????????? .attr( "x" , function (val,index){ ???????????????? return ?30*index+50; ???????????? }) ???????????? .attr( "y" , function (val,index){ ???????????????? return ?300-linear(val)-50; ???????????? }) ???????????? .attr( "width" ,25) ???????????? .attr( "height" , function (val,index){ ???????????????? return ?linear(val); ???????????? });? </script> |
結(jié)果如下:
