本文目前来说,是学完极客学院的《D3.js 入门教程》之后的整理出来的精简知识版,仅仅是为了知识整理。后期我会随着学习的深入,在这个基础上,进行维护与更改。
我们先来了解一下力导向图(Force-Directed Graph)的定义。首先,它是绘图的一种算法。在二维或三维空间里配置节点。节点之间用线连接,称为连线。各连线的长度几乎相等,且尽可能不相交。节点和连线都被施加了力的作用。力的大小是根据节点和连线的相对位置计算的。根据力的作用来计算节点和连线的运动轨迹,并不断降低它们的能量,最终达到一种能量很低的安定状态。
功能上,力导向图能表示节点之间的多对多的关系。
力导向图的原生数据由节点(nodes)和连线(edges)的两个数组组成。nodes 代表节点的一些必要信息,而 edges 则表示的是节点之间的连线与否。请看下列实例数据:
var nodes = [{ name: "桂林" }, { name: "广州" }, { name: "厦门" }, { name: "杭州" }];
var edges = [{ source: 0, target: 1 }, { source: 0, target: 2 }, { source: 0, target: 3 }];
注意:**edges 里面包含着各个节点之间的连线关系的对象。*source 表示的是连线的起源,target 表示的是连线的结尾,它们的值就是 *nodes 中对应节点对象的下标。
但是这样的数据不能用于作图,因为目前的数据还没有节点和连线的坐标,能让我们去定位。于是,我们自然而然就想到了布局函数。制作力导向图的布局函数为:d3.layout.force()
。对于力导向图,我们一般如此布局:
var force = d3.layout.force() // 生成力导向图的布局函数
.nodes(nodes) // 指定节点数组
.links(edges) // 指定连线数组
.size([ width, height ]) // 指定作用域范围
.linkDistance(150) // 指定连线长度
.charge([-400]); // 相互之间的作用力
ok ,现在我们来看看 nodes 和 edges 分别变成了什么样子:
这是 nodes 布局后的变化,我们再来看看 edges 布局后的变化:
仔细看可以发现布局后的节点对象和连线对象都在每个对象中增加了下列属性:
有了布局后的 nodes 和 edges 数据后,就可以绘制力导向图了。绘制力导向图分别需要绘制三种图形元素:
以下是示例代码:
// 添加连线
var svg_edges = svg.selectAll('line').data(edges).enter().append('line');
var color = d3.scale.category20();
// 添加节点
var svg_nodes = svg.selectAll('circle').data(nodes).enter().append('circle')
.attr('r', 10) // 圆半径为10
.style('fill', function ( d, i ) { // 给每个圆添加颜色
return color(i);
})
.call(force.drag); // 使得节点能够拖动
var svg_texts = svg.selectAll('text').data(nodes).enter().append('text')
.attr('dx', 12) // 将text元素定位在circle元素的左侧
.attr('dy', 8)
.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; });
});
这样一个力导向图便基本算完成了。