免费商城版网站制作网络优化工程师前景如何
文章目录
- D3.js 简单介绍
- 选择集与方法
- 数据绑定方法
- 选择集添加DOM元素以及删除元素
- 理解update enter 以及 exit
- 关于比例尺
- layout 布局
- force layout
- 坐标轴元素
- 添加动态效果
- demo1: 绘制简单柱状图
#D3.js 初探
最近在做一个Data Visualization 的项目,由于对最终呈现的效果的交互性要求比较高,再加上自己做过前端的项目,对javascript还是比较熟悉的,所以最终采用D3.js 来完成数据的可视化。在工作之余花了点时间系统学习了一下D3.js,接下来几篇blog会对d3.js 做一些由简到繁的一些总结。
D3.js 简单介绍
D3.js 是一款数据可视化工具,说到数据可视化工具那简直不要太多,这里没必要一一列出。我主要说明一下我对D3.js 的一些认识。D3.js 是一个javascript 的函数库(称不上框架),地位类似于JQuery,只是区别在于一个是做可视化的库,一个是做纯前端开发的库,但最终目的都是方便开发者能够快速完成功能。而且本身底层就是javascript实现,非常适合于懂web前端开发并且又需要完成数据可视化任务的人;相比于Python的数据可视化工具,个人更倾向于使用D3.js。
选择集与方法
D3.js底层依然是操作DOM元素,所以依然需要选择操作的DOM元素实现绑定数据,添加事件,添加动画效果等等。D3.js 里面提供两种方法去选择元素d3.select
和 d3.selectAll
方法名称其实就表明了第一种方法只是选择匹配上的第一个元素,而第二种方法会选择匹配上的所有元素;两种方法参数就是CSS选择器参数相同,例子如下:
<body><p >hello</p><p >world</p>
</body>
var body = d3.select('body');// get body
// change the first p
var p = body.select('p');
p.text('just change the first p');
// change all the p
var p = body.selectAll('p')
p.text('just change all the p')
数据绑定方法
D3.js 也提供了非常不错的数据绑定的方法,方便开发者完成DOM元素与数据的绑定,主要有两个方法datum
和 data
区别在于第一个函数将一个数据与所有选中元素绑定;而第二个函数是将一个列表与选中的所有元素绑定,从使用情况来看,方法二使用更为频繁。例子如下
var body = d3.select('body');var p = body.selectAll('p');data = 'hello';p.datum(data);p.text(function(d,i){return 'number '+i+' is '+d;//其中d为当前元素绑定数据,i为元素索引。});
输出:
number 0 is hellonumber 1 is hello
绑定数据唯一所以在text的回调函数里面d这个参数传递内容都是一样的。如果采用data
函数又是如下的效果:
number 0 is hellonumber 1 is how
选择集添加DOM元素以及删除元素
选择集中添加DOM元素提供了两种方法append
和 insert
方法一是在选择集内部后面插入元素,方法二是在选择集内部前面插入元素。注意不是平级别的,如果选择只是一个元素,那么在这个元素里面插入,如果选择的是一个集合,那么会在集合中的每一个元素内部在插入元素。例子如下:
<body><p>this is the first p</p><p>this is the second p</p><script>var body = d3.select('body');var p = body.selectAll('p');data = ['how','are','you'];p.data(data);p.text(function(d,i){return d;});p.append('p').text('hello');//每一个选中的p中都会再插入一个p元素并且内容为hello</script></body>
输出结果:
this is the first p
this is the second p
``` 输出结果:理解update enter 以及 exit
update enter 以及 exit 是D3.js 里面非常重要的概念。用于处理当数据数量与元素数量关系不一致的情况。
update 表示DOM元素与绑定数据一一对应的部分。可以这样理解再任何选择集合上面调用data
方法绑定数据之后返回的其实都是update部分。而enter指的是当集合中元素小于绑定数据时候,D3.js 会创建一些空的"元素空间"和绑定数据对应,以便于增加新的元素最终保持数量与绑定的数据的量一致;而exit正好是另外一种情况,当DOM元素数量多于绑定数据数量时,exit值得正式那一部分多出的数据。一般正对exit的部分都直接调用remove函数,因为没有必要保留没有绑定数据的DOM元素。例子如下:
html如下
<body onload="load()"><p >this is the first p</p><p>this is the second p</p><p> this is third p</p>
</body>
javascript代码如下:
function load(){var p = d3.select('body').selectAll('p');data = [1,2,3,4,5,6];p.data(data) //获取到了update部分 .text(function(d,i){return 'update content '+d+' and'+i;}).enter() //获取到enter 部分.append('p') //针对剩余的数据添加p元素并绑定.text(function(d,i){return 'enter content '+d+' and'+i;});p.append('p').text('shows?');// 此时p 只是代表update 部分所以调用append 只会在前三个p内部添加元素,后面enter 部分添加的元素不会再次添加}
输出的结果为:
同样exit的例子如下,处理当DOM元素数量大于绑定数据量的情况:
<body onload="load()"><p >this is the first p</p><p>this is the second p</p><p> this is third p</p>
</body>
javascript:
function load(){var p = d3.select('body').selectAll('p');data = [1,2];p.data(data)//获取到了update部分 .text(function(d,i){return 'update content '+d+' and'+i;}).exit() //获取到exit部分.text(function(d,i){return 'exit content '+d+' and'+i;});}
输出结果为:
可以看到一个有趣的现象,由于最后一个元素没有绑定数据,所以回调函数中传入的参数d为undefined可以理解,但是i这个表示Index的参数还持续生效。
关于比例尺
之前使用过简单的数据绑定,在绘制图形的时候是直接使用数据的大小表示图形中的像素大小,但是在某一些情况下却不能够这么做(比如数据list本身数据都很小,或者是都非常大) 此时就需要使用比例尺将数据等价转换成可以很好的可视化的数据。比例尺可以看做一个映射关系,即为X到Y的映射关系其中X称为定义域(domain) Y 称为值域(range)。再D3.js 中主要提供了两种比例尺:线性比例尺以及序列比例尺。
- 线性比例尺可以看做是一个线性变换,例子如下:
function load(){src_data = [0.2,0.3,0.1,0.4,0.5]; //原始数组max_value = d3.max(src_data); //获取原始数据的最大值与最小值,作为定义域范围min_value = d3.min(src_data);var linear_scale = d3.scale.linear().domain([min_value,max_value]).range([0,50]);//构建一个线性的比例尺函数for(var i=0;i<src_data.length;i++){console.log('原始数据'+src_data[i]);console.log('线性比例尺变换结果'+linear_scale(src_data[i]);}}
返回的线性比例尺为一个函数,传递原始数据范围内的任意一个数值都可以映射到Range范围内相应的一个数值。所以实际操作中只需要知道原始数据的最大值和最小值以及最终Range的范围就可以确定这个比例尺函数。线性比例尺适用于domain以及Range都是连续数值的应用场景。
2.序数比例尺
序数比例尺适用于domain 和Range 取值都是离散的应用情况,只是单纯的从domain中的元素一一映射到range中的元素。
var ord_src = [0,1,2,3,4]var ord_ran = ['red','blue','white']var ordinal_scale = d3.scale.ordinal().domain(ord_src).range(ord_ran);for(var i=0;i<ord_src.length;i++){console.log('src data'+ord_src[i]);console.log('ordinal data'+ordinal_scale(ord_src[i]));}
从输出来看即便Range的元素少于domain元素,最终结果依然是循环映射,不会出现undefined的情况。
layout 布局
layout布局元素是为了实现更加强大而多样化的可视化功能,D3.js 中有非常多的layout。下面以force layout(力量图)作为一个demo进行讲解
force layout
force layout 即是力量图,此种图形中的元素是可以进行拖拽的。demo如下:
一一讲解demo中调用函数的意义:
坐标轴元素
D3提供了坐标轴,使用d3.svg.axis()
可以构建坐标轴,本质上这一段代码返回的是一个函数,后续跟scale()确定比例尺orient()指定刻度位置,ticks()指定刻度数量。
var margin = {'left':100,'top':20,'bottom':20,'right':20};var body = d3.select('body');var svg = body.append('svg').attr('width',200).attr('height',200).style('margin-left',margin.left+'px').style('margin-top',margin.top+'px').style('padding',20).style('background-color','yellow');var min_val = 0;var max_val = 40;// domain shows the ticks value and range shows the length(px) of the axisvar linear_scale = d3.scale.linear().domain([min_val,max_val]).range([0,200]);var axis = d3.svg.axis().scale(linear_scale).orient('bottom').ticks(6);svg.append('g').call(axis);// add the axis
添加动态效果
图标的动态效果在可视化中是非常关键而重要的,一张静态的图并不能够很好的反应出数据的一些变化规律,所以需要一些动态的效果辅助改特性的展现,数据应该是“动”的。
1.transation
过度效果,其实本身用CSS以及JavaScript都能够实现这些功能,D3.js也只是做了一些封装,保证编程风格一致。transition用于启动过度效果,实现增加元素一个状态(位置或者颜色)到另外一个状态的动态感。
2.duration
过度持续时间,单位为毫秒,一些状态的变换需要较长的时间来凸显效果。
3.ease
设定过度的方式,常用的方式为:1)linear线性变化,circle缓慢变化到最终状态,elastic弹性变化,bounce变换状态终点时弹几次。
4.delay
指定过度的延迟时间,单位毫秒,给定回调函数可以指定特定的元素过度效果的延迟时间。
简单的例子如下:
demo1: 绘制简单柱状图
一般使用D3.js 绘制图标都会选择在SVG上面绘制,使用矢量图即可以保证放大缩小图像不失真,又能够保证图形中每一个元素都能够添加事件,提高了图形的交互能力。有一点需要注意SVG中X轴正方向是水平向右,而Y轴正方向是竖直向下。绘制简易的柱状图如下:
function load(){var height = 200,width = 200;var rectheight = 30;var body = d3.select('body');var svg = body.append('svg').attr('width',width).attr('height',height);data = [100,50,45,120,300];//表示柱状图的宽度。svg.append('rect');// 添加一个初始空的rect 便于统一选择并且设置属性与文本,DOM上面只是增加一个空白的rectsvg.selectAll('rect').data(data).enter().append('rect') //enter 部分添加rect.attr('x',10) //设置每一个rectx 坐标.attr('y',function(d,i){return i*rectheight;}) //设置每一个rect y坐标.attr('width',function(d){ return d;})// 设置每一个rect 宽度.attr('height',rectheight-5)// 设置每一个rect 高度.attr('fill','blue');// 设置每一个rect 颜色}