d3.js官网https://d3js.org/。全称为“data-driven Documents”,3个d嘛,简称d3.js。使用的话,官网上面有下载连接,就是一个zip压缩包。解压后把里面的d3.js引入就可以使用了,它并需要使用npm来安装,就是一个普通的js文件罢了,就像我们使用jquery,下载下来引入就可以使用了,方法是一样的。
d3.js是干嘛的呢?
大家有用过类似的图表展现工具吧,比如百度的echarts,国外的highcharts之类的,d3.js跟这些图标插件很类似,都是用来做数据可视化的,常说“一张图胜过千言万语”,在如今的大数据时代,数据的可视化显得尤为重要,而d3.js在这方面做得很是突出,d3.js也被称为操作svg的jquery,因为d3.js的图表都是基于svg的,在操作svg方面的语法与jquery的方法很是类似。
我们来看一个例子,实现的功能是使用d3.js在网页中写入“hello d3.js”,网页结构如下:
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> |
打开这个网页:
![image.png](https://i-blog.csdnimg.cn/blog_migrate/3604c17da0eddf8d7df4ed1e06d57707.png)
使用d3.js在页面上画个圆圈
为了熟悉d3.js的api,我们通过一个小小的案例来领略一下d3.js的风采。这个案例很简单,就是使用d3.js在页面上画一个圆形,最终的效果如下图所示:
![image.png](https://i-blog.csdnimg.cn/blog_migrate/227e020b112e95a8f7f90cc2a8ab6b55.png)
以下是代码:
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元素尾部添加一个svg标签 var svg=body.append("svg").attr("width",300).attr("height",300); //svg尾部添加一个circle标签并设置属性 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文档中的元素,里面的参数是css选择器,返回值是单个的经过d3封装的html元素。
append方法用于在调用者尾部附加一个元素,
attr(属性名,属性值)用于设置调用该方法的元素的属性,具体属性名是什么,自然跟调用该方法的元素有关,比如width和height都是svg元素有的属性,而下面的cx,cy都是svg里面的cricle标签的属性,可不是我随便乱写的。
学习过我前边写的svg相关的教程的同学都知道,最后一行的fill,stroke这些都是样式,对这些样式的控制我们最好还是写在css里,然后使用d3.js把这个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元素尾部添加一个svg标签 var svg=body.append( "svg" ).attr( "width" ,300).attr( "height" ,300); //svg尾部添加一个circle标签并设置属性 svg.append( "circle" ).attr( "cx" ,100).attr( "cy" ,100).attr( "r" ,50) .attr( "class" , "mycircle" ); </script> </html> |
d3的update对象、enter对象、exit对象
先看下面的例子:
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是经过d3包装的元素数组 console.log(divs); var arr=["a","b","c"]; var update=divs.data(arr); //update对象是已经绑定了数据集的选择集 console.log(update); update.text(function(val,index){ return "坐标:"+index+",值:"+val; }); </ script > </ html > |
执行结果:
![image.png](https://i-blog.csdnimg.cn/blog_migrate/dff768b4d3b2d7c546821875a53ebfc4.png)
selectAll:返回的是经过d3包装的元素数组,称为d3的选择集
data用于把数组的元素依次绑定到选择集的元素上,有点类似es6里面解构赋值的意思。
datum也是绑定数据,和data()的区别在于datum是直接绑定数据到选择集上,并不存在"解构",如divs.datum(arr);则每个div将被绑定a,b,c
上面我说了,update对象就是绑定了数据集的选择集,在上面的这个例子中,选择集的长度是3,数据集的长度也是3,正好可以一一对应,但是现实的业务不可能这么完美,两者总有不相等的时候。
一、当选择集divs的大小>数据集arr的长度时
这个时候如果要想让他们一一对应,我们应该把divs中的多余的元素给找到并删除,可以这样理解,在d3.js中找到的多余的选择集里面的元素就是exit对象,把数据arr改为两个长度:
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是经过d3包装的元素数组 console.log(divs); var arr=["a","b"]; var update=divs.data(arr); //update对象是已经绑定了数据集的选择集 console.log(update); update.text(function(val,index){ return "坐标:"+index+",值:"+val; }); //此时有多余的div,找到(exit)并删除 var exit= update.exit(); console.log(exit); exit.remove(); </ script > </ html > |
添加exit.remove();前后的结果对比:
二、当选择集divs的大小<数据集arr的长度时
此时要想对应,需要多补几个选择集的元素,补几个?补什么元素?都是通过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是经过d3包装的元素数组 console.log(divs); var arr=["a","b","c","d","e"]; var update=divs.data(arr); //update对象是已经绑定了数据集的选择集 console.log(update); update.text(function(val,index){ return "坐标:"+index+",值:"+val; }); //增加enter对象 //enter对象可以获取数据集多几个元素 var enter= update.enter(); console.log(enter); //多几个就补几个p enter.append("p").text(function(val,index){ return "坐标:"+index+",值:"+val; }); </ script > </ html > |
增加enter对象前后结果对比:
使用d3画一个没有刻度的柱形图
最终的效果如下:
![image.png](https://i-blog.csdnimg.cn/blog_migrate/fb1edfa5ea836df81158f029ec7d75ca.png)
代码如下,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;
})
这一段,这一段的作用是让各个柱子都处在离svg下边缘50px的位置,也就是说让所有柱子都位于一条水平线上,如果不这样,那就不叫柱形图了,变成k线图了....,为什么写上“300-val-50”就可以位于一条水平线上了呢?看下面的图:
![image.png](https://i-blog.csdnimg.cn/blog_migrate/1693f14d676d047bc1834614f1c61744.png)
rect的y坐标是从上往下数的,这样子就应该理解了吧。
柱形图的完善---引入d3.js中的比例尺
在d3.js中,有很多种比例尺,常用的有两种。什么叫做比例尺呢?这个我相信大家心里都有个内化于心的概念,在d3.js中,我说的直白点,就是把一组值映射为另一组值,在这个映射的过程中,关系保持不变。那么为什么需要比例尺呢?在“使用d3画一个没有刻度的柱形图”上篇文章里面,设置柱状图的高度,我使用的代码是
1 2 3 | attr( "height" , function (val,index){ return val; } |
也就是说,我直接使用了数据集里面元素的值作为柱状图的高度,因为数据集是var dataset=[20,30,70,10,50];整个svg大小我设置的宽高分别为500px和300px,并没有看出问题,可是我并不能担保在真正使用的时候数据集永远不会超过svg的大小,也就是说数据集里面有了个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(),不管哪种写法,只是版本的不同罢了,返回的都是比例尺的对象,这个对象是个函数,因此我们后边可以直接使用linear(1500)的方式来写,其实就是调用的函数,参数是原来的数字,返回值为经过比例尺转化后的数字。domain([最小,最大])指定原来的值范围,range([最小,最大])指定映射范围。
二、序数比例尺
上边的线性比例尺指定的是范围、区间,可以映射这个区间内任意的值。但是,有时候我们需要映射的可能是离散的值,比如我想把[1,2,3]映射为["a","b","c"];使用线性比例尺就不行了,而应该使用序数比例尺。
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了,导致看不到 .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> |
结果如下:
![image.png](https://i-blog.csdnimg.cn/blog_migrate/7f56e6087b39ceeffe839937f3366ce7.png)