d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

 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>

打开这个网页:

d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

使用d3.js在页面上画个圆圈

 为了熟悉d3.js的api,我们通过一个小小的案例来领略一下d3.js的风采。这个案例很简单,就是使用d3.js在页面上画一个圆形,最终的效果如下图所示:

    d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

以下是代码:

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>

执行结果:

d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

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();前后的结果对比:

d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象 d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

二、当选择集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.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

 

 

使用d3画一个没有刻度的柱形图

最终的效果如下:

d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

代码如下,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”就可以位于一条水平线上了呢?看下面的图:

d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象

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>

结果如下:

d3.js介绍以及使用d3.js画一个圆圈和柱状图附带讲解d3.js的enter对象、update对象、exit对象