学习一项新技术,首先要搞清楚它的基本设计思路,有了这个宏观的技术架构,使用该技术起来,就会得心应手了。否则,就会不知道如何下手,即使看到人家的例子程序,可能也不知其所以然。
下面,就简单的结合自己研究的官方文档,对D3的使用,给其基本设计思路,做一个简单的描述:
在html文件中加载完官方的js插件d3.v3.min.js后:
1. 设计画布(这里主要基于SVG介绍,当然,画布其实还有Canvas)。
a. 主要包括SVG的大小,含有width,以及height。
b. 通过d3全局对象,构建svg对象。
2. 创建视图(D3支持的视图很多)
1 [[bundle|捆绑布局]] - 对边使用Holten 层次捆绑算法。 2 [[chord|弦布局]] - 从关系矩阵生成一个弦图。 3 [[cluster|簇布局]] - 将实体聚集成树状图。 4 [[force|力布局]] - 模拟物理力排放相连节点的位置。 5 [[hierarchy|层次布局]] - 这是个抽象布局,可派生一个定制的层次布局。 6 [[histogram|直方图布局]] - 使用量化的箱计算数据的分布。 7 [[pack|包布局]] - 用递归的圆形包装生成一个层次布局。 8 [[partition|分区布局]] - 递归地将节点树分割为旭日状或者冰柱状。 9 [[pie|饼布局]] - 计算饼图或圆环图中弧的开始和结束角度。 10 [[stack|堆叠布局]] - 计算堆叠图或者面积图的基线。 11 [[tree|树布局]] - 整齐地排列树节点。注意簇布局不是整齐的。 12 [[treemap|矩形树布局]] - 使用空间递归分区算法展示树的节点。
例如:
var tree = d3.layout.tree();
3. 数据绑定
其实,这一步做的事情,就是一个视图与数据的结合,视图将数据转化为该视图内定的数据结构,重点是添加了一些D3功能相关的辅助参数或者变量。 例如上例中的树视图,数据绑定后过程如下:
1 var nodes = tree.nodes(root).reverse(), 2 var links = tree.links(nodes);
这个绑定,也可以说是数据转化,之后,nodes对象中不仅含有root这个原始数据(用户提供的输入数据),还含有D3系统需要的辅助参数,比如parent,depth等。
4. 视图数据结合
这一步,其实最能体现D3的设计思想,这一步涵盖的内容最为丰富,但是不多讲,重点介绍D3的特色操作enter,update,exit。
a. 获取新的选择器(SVG灌入绑定数据后的新对象)
例如:
var node = svg.selectAll("g.node").data(nodes, function(d) { return d.id || (d.id = ++i); });
b. enter/update/exit
这三个概念是什么含义呢?
如果数组为 [3, 6, 9, 12, 15],将此数组绑定到三个 p 元素的选择集上。可以想象,会有两个数据没有元素与之对应,这时候 D3 会建立两个空的元素与数据对应,这一部分就称为 Enter。而有元素与数据对应的部分称为 Update。如果数组为 [3],则会有两个元素没有数据绑定,那么没有数据绑定的部分被称为 Exit。
例如下图:
对于图形存在交互操作的情景,enter,update,以及exit都会用得到,例如我上一篇博客D3树状图异步按需加载数据,里面的树状图,点击节点会有展开和收缩的效果,这个时候三者都会参与实际的逻辑处理。
Update 和 Enter 的使用
当对应的元素不足时 ( 绑定数据数量 > 对应元素 ),需要添加元素(append)。
现在 body 中有三个 p 元素,要绑定一个长度大于 3 的数组到 p 的选择集上,然后分别处理 update 和 enter 两部分。
1 var dataset = [ 3 , 6 , 9 , 12 , 15 ]; 2 3 //选择body中的p元素 4 var p = d3.select("body").selectAll("p"); 5 6 //获取update部分 7 var update = p.data(dataset); 8 9 //获取enter部分 10 var enter = update.enter(); 11 12 //update部分的处理:更新属性值 13 update.text(function(d){ 14 return "update " + d; 15 }); 16 17 //enter部分的处理:添加元素后赋予属性值 18 enter.append("p") 19 .text(function(d){ 20 return "enter " + d; 21 });
- update 部分的处理办法一般是:更新属性值
- enter 部分的处理办法一般是:添加元素后,赋予属性值
Update 和 Exit 的使用
当对应的元素过多时 ( 绑定数据数量 < 对应元素 ),需要删掉多余的元素。
现在 body 中有三个 p 元素,要绑定一个长度小于 3 的数组到 p 的选择集上,然后分别处理 update 和 exit 两部分。
1 var dataset = [ 3 ]; 2 3 //选择body中的p元素 4 var p = d3.select("body").selectAll("p"); 5 6 //获取update部分 7 var update = p.data(dataset); 8 9 //获取exit部分 10 var exit = update.exit(); 11 12 //update部分的处理:更新属性值 13 update.text(function(d){ 14 return "update " + d; 15 }); 16 17 //exit部分的处理:修改p元素的属性 18 exit.text(function(d){ 19 return "exit"; 20 }); 21 22 //exit部分的处理通常是删除元素 23 // exit.remove();
- exit 部分的处理办法一般是:删除元素(remove)
到此, 入门D3,了解这个基本的框架,再去细读API文档或者教程,相对会容易些!
最后,附带一个简单的例子,绘制环状图(显示资源占用比例的场景,还是很有使用价值的):
1 <html> 2 <head> 3 <meta charset="utf-8"> 4 <title>环装图</title> 5 </head> 6 7 <style> 8 9 10 </style> 11 <body> 12 <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 13 <script> 14 15 var width = 400; 16 var height = 400; 17 var dataset = [ 30 , 10 , 43 , 55 , 13 , 11, 100, 99]; 18 19 var svg = d3.select("body") 20 .append("svg") 21 .attr("width", width) 22 .attr("height", height); 23 24 var pie = d3.layout.pie(); 25 26 var piedata = pie(dataset); 27 28 var outerRadius = 150; //外半径 29 var innerRadius = 100; //内半径,为100则中间没有空白 30 31 var arc = d3.svg.arc() //弧生成器 32 .innerRadius(innerRadius) //设置内半径 33 .outerRadius(outerRadius); //设置外半径 34 35 var color = d3.scale.category20c(); 36 37 var arcs = svg.selectAll("g") 38 .data(piedata) 39 .enter() 40 .append("g") 41 .attr("transform","translate("+ (width/2) +","+ (width/2) +")"); 42 43 arcs.append("path") 44 .attr("fill",function(d,i){ 45 return color(i); 46 }) 47 .attr("d",function(d){ 48 return arc(d); 49 }); 50 51 arcs.append("text") 52 .attr("transform",function(d){ 53 return "translate(" + arc.centroid(d) + ")"; 54 }) 55 .attr("text-anchor","middle") 56 .text(function(d){ 57 return d.data; 58 }); 59 </script> 60 61 </body> 62 </html>