D3.js系列——布局:饼状图和力导向图
一、饼状图
在布局的应用中,最简单的就是饼状图。
1、数据
有如下数据,需要可视化:
var dataset = [ , , , , ];
这样的值是不能直接绘图的。例如绘制饼状图的一个部分,需要知道一段弧的起始角度和终止角度,这些值都不存在于数组 dataset 中。因此,需要用到布局,布局的作用就是:计算出适合于作图的数据。
2、布局(数据转换)
定义一个布局:
var pie = d3.layout.pie();
返回值赋给变量 pie,此时 pie 可以当做函数使用。
var piedata = pie(dataset);
将数组 dataset 作为 pie() 的参数,返回值给 piedata。如此一来:piedata 就是转换后的数据。简单吧,非常轻松就得到了。在控制台输出 piedata ,让我们来看看数据转换成什么了。
如上图所示,5 个整数被转换成了 5 个对象(Object) ,每个对象都有变量起始角度(startAngle)和终止角度(endAngle),还有原数据(属性名称为 data)。这些都是绘图需要的数据。
千万记住:布局不是要直接绘图,而是为了得到绘图所需的数据。
3、绘制图形
为了根据转换后的数据 piedata 来作图,还需要一样工具:生成器。
SVG 有一个元素,叫做路径 <path>,是 SVG 中功能最强的元素,它可以表示其它任意的图形。顾名思义,路径元素就是通过定义一个段“路径”,来绘制出各种图形。但是,路径是很难计算的,通过布局转换后的数据 piedata 仍然很难手动计算得到路径值。为我们完成这项任务的,就是生成器。
这里要用到的叫做弧生成器,能够生成弧的路径,因为饼图的每一部分都是一段弧。
var outerRadius = ; //外半径
var innerRadius = ; //内半径,为0则中间没有空白 var arc = d3.svg.arc() //弧生成器
.innerRadius(innerRadius) //设置内半径
.outerRadius(outerRadius); //设置外半径
弧生成器返回的结果赋值给 arc。此时,arc 可以当做一个函数使用,把 piedata 作为参数传入,即可得到路径值。
接下来,可以在 SVG 中添加图形元素了。先在 <svg> 里添加足够数量(5个)个分组元素(g),每一个分组用于存放一段弧的相关元素。
var arcs = svg.selectAll("g")
.data(piedata)
.enter()
.append("g")
.attr("transform","translate("+ (width/) +","+ (width/) +")");
接下来对每个 <g> 元素,添加 <path>
arcs.append("path")
.attr("fill",function(d,i){
return color(i);
})
.attr("d",function(d){
return arc(d); //调用弧生成器,得到路径值
});
因为 arcs 是同时选择了 5 个 <g> 元素的选择集,所以调用 append(“path”) 后,每个 <g> 中都有 <path> 。路径值的属性名称是 d,调用弧生成器后返回的值赋值给它。要注意,arc(d) 的参数 d 是被绑定的数据。
另外,color 是一个颜色比例尺,它能根据传入的索引号获取相应的颜色值,定义如下。
var color = d3.scale.category10(); //有十种颜色的颜色比例尺
然后在每一个弧线中心添加文本。
arcs.append("text")
.attr("transform",function(d){
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor","middle")
.text(function(d){
return d.data;
});
arc.centroid(d) 能算出弧线的中心。
要注意,text() 里返回的是 d.data ,而不是 d 。因为被绑定的数据是对象,里面有 d.startAngle、d.endAngle、d.data 等,其中 d.data 才是转换前的整数的值。
二、力导向图
力导向图中每一个节点都受到力的作用而运动,这种是一种非常绚丽的图表。
力导向图(Force-Directed Graph),是绘图的一种算法。在二维或三维空间里配置节点,节点之间用线连接,称为连线。各连线的长度几乎相等,且尽可能不相交。节点和连线都被施加了力的作用,力是根据节点和连线的相对位置计算的。根据力的作用,来计算节点和连线的运动轨迹,并不断降低它们的能量,最终达到一种能量很低的安定状态。
力导向图能表示节点之间的多对多的关系。
1、数据
初始数据如下:
var nodes = [ { name: "桂林" }, { name: "广州" },
{ name: "厦门" }, { name: "杭州" },
{ name: "上海" }, { name: "青岛" },
{ name: "天津" } ]; var edges = [ { source : , target: } , { source : , target: } ,
{ source : , target: } , { source : , target: } ,
{ source : , target: } , { source : , target: } ];
节点(nodes)和连线(edges)的数组,节点是一些城市名,连线的两端是节点的序号(序号从 0 开始)。
这些数据是不能作图的,因为不知道节点和连线的坐标。这句话一说出来,就请想到布局。力导向图用到的布局是:d3.layout.force()。
2、布局(数据转换)
定义一个力导向图的布局如下
var force = d3.layout.force()
.nodes(nodes) //指定节点数组
.links(edges) //指定连线数组
.size([width,height]) //指定作用域范围
.linkDistance() //指定连线长度
.charge([-]); //相互之间的作用力
然后,使力学作用生效:
force.start(); //开始作用
如此,数组 nodes 和 edges 的数据都发生了变化。在控制台输出一下,看看发生了什么变化。
节点转换前后如下图
转换后,节点对象里多了一些变量。其意义如下:
- index:节点的索引号
- px, py:节点上一个时刻的坐标
- x, y:节点的当前坐标
- weight:节点的权重
再来看看连线的变化:
可以看到,连线的两个节点序号,分别变成了对应的节点对象。(一个代表源,一个代表目标)
3、绘制
有了转换后的数据,就可以作图了。分别绘制三种图形元素:
- line,线段,表示连线。
- circle,圆,表示节点。
- text,文字,描述节点。
代码如下:
//添加连线
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",); var color = d3.scale.category20();//颜色比例尺 //添加节点
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r",20)
.style("fill",function(d,i){
return color(i);
})
.call(force.drag); //使得节点能够拖动 //添加描述节点的文字
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", )
.attr("dy", )
.text(function(d){
return d.name;
});
调用 call( force.drag ) 后节点可被拖动。force.drag() 是一个函数,将其作为 call() 的参数,相当于将当前选择的元素传到 force.drag() 函数中。
最后,还有一段最重要的代码。由于力导向图是不断运动的,每一时刻都在发生更新,因此,必须不断更新节点和连线的位置。
力导向图布局 force 有一个事件 tick,每进行到一个时刻,都要调用它,更新的内容就写在它的监听器里就好。
force.on("tick", function(){ //对于每一个时间间隔
//更新连线坐标
svg_edges.attr("x1",function(d){ return d.source.x; })
.attr("y1",function(d){ return d.source.y; })
.attr("x2",function(d){ return d.target.x; })
.attr("y2",function(d){ return d.target.y; }); //更新节点坐标
svg_nodes.attr("cx",function(d){ return d.x; })
.attr("cy",function(d){ return d.y; }); //更新文字坐标
svg_texts.attr("x", function(d){ return d.x; })
.attr("y", function(d){ return d.y; });
});
tick 的英文意思是钟表发出的嘀嗒嘀嗒声,每次触发时,都会调用后面的无名函数 function。
最新文章
- Android之实现ViewPagerIndicator
- JavaScript事件冒泡简介及应用
- wine on ubuntu linux, and source insight 绿色版的安装
- 深入Java虚拟机读书笔记第一章Java体系结构介绍
- 节点地址的函数list_entry()原理详解
- Linux游(1): diff, patch和quilt (下一个)
- uoj#38. 【清华集训2014】奇数国【欧拉函数】
- CodeForces 543D:Road Improvement
- OpenCASCADE Texture Mapping
- c/c++ lambda 表达式 介绍
- JavaEE学习之Maven配置文件pom.xml详解(转)
- Python学习(十三) —— 网络编程
- caffe运行错误: im2col.cu:61] Check failed: error == cudaSuccess (8 vs. 0) invalid device function
- Python Appium 元素定位方法简单介绍
- kohana task 编写计划任务
- OpenState: Programming Platform-independent Stateful OpenFlow Applications Inside the Switch
- WPF 自定义下拉列表
- LightOJ 1214 Large Division
- Altium Designer (AD) 中规则的部分讲解
- Java基础知识:Java实现Map集合二级联动4
热门文章
- [ Python - 15 ] win7安装paramiko问题总汇
- JavaSript中数组方法是否对原数组产生影响
- js实现侧边栏信息展示效果
- c语言中Triplet是什么意思?
- 关于ofbiz加载数据模块的文件参数配置
- 【cocos2d-js官方文档】九、cc.loader
- thinkphp函数学习(1)——header, get_magic_quotes_gpc, array_map, stripslashes, stripslashes_deep
- centos 修改ftp目录
- strcmp()函数-比较字符串的大小、字符串排序
- Opencv利用Mat访问像素值