代码操作一共两处
var simulation = d3 .forceSimulation() .force( "link", d3.forceLink().id(function(d) { return d.id; }) ) .force("charge", d3.forceManyBody()) // 电荷距离和电荷强度 .force("center", d3.forceCenter(width / 2, height / 2));复制代码
simulation.nodes(graph.nodes).on("tick", ticked);simulation.force("link").links(graph.links);复制代码
效果图
布局的一些基本共识
通常我们通常可以通过以下标准来衡量一个图布局算法的好坏:
- 交叉的边要尽量少
- 边长要尽量均衡
- 布局要尽量对称
- 单位面积能放尽量多的结点
以上标准前面的更重要,而且基本上是满足一些就会减弱另一些,而力引导算法基本上能很好的平衡,所以它算是一个很好的图布局算法。
本文来自 wry2008wry 的CSDN 博客 ,全文地址请点击:
测试数据
{ "nodes": [ { "id": "name0", "group": 1}, { "id": "name1", "group": 1}, { "id": "name2", "group": 1}, { "id": "name3", "group": 1}, { "id": "name4", "group": 1}, { "id": "name5", "group": 1}, { "id": "name6", "group": 1}, { "id": "name7", "group": 1} ], "links": [ { "source": "name0", "target": "name1", "value": 1}, { "source": "name0", "target": "name2", "value": 1}, { "source": "name0", "target": "name3", "value": 1}, { "source": "name0", "target": "name4", "value": 1}, { "source": "name1", "target": "name5", "value": 1}, { "source": "name1", "target": "name6", "value": 1}, { "source": "name5", "target": "name7", "value": 1} ] } 复制代码
关于force代码的目录
ps:
- link.js - 实现“连接力”,即当两个结点间有边时相互拉拢,但也不能太近;
- manyBody.js - 实现“多体”力,用于模拟引力及静电力;
- radial.js - “圆环力”,实现半径布局即结点分布在一个圆环上;
- center.js - “中心力”实现,实现结点向中心点收拢;
- index.js总的入口
- simulation就是我们调用d3.forceSimulation()的时候发生的
代码的调用流程以简单的center.js为例。
(1) 代码 d3.forceSimulation() 调用simulation.js 返回了一个叫simulation的对象,还有nodes和force等方法。
(4) 代码tick由于力导向图是不断运动的,每一时刻都在发生更新,因此,必须不断更新节点和连线的位置。 力导向图布局 force 有一个事件 tick,每进行到一个时刻,都要调用它,更新的内容就写在它的监听器里就好
(5) simulation.js中的tick会不停的执行,遍历forces。更新坐标位置。
function tick() { var i, n = nodes.length, node; console.log('----<><>') alpha += (alphaTarget - alpha) * alphaDecay; forces.each(function(force) { force(alpha); }); for (i = 0; i < n; ++i) { node = nodes[i]; if (node.fx == null) node.x += node.vx *= velocityDecay; else node.x = node.fx, node.vx = 0; if (node.fy == null) node.y += node.vy *= velocityDecay; else node.y = node.fy, node.vy = 0; } }复制代码
对代码的数据进行跟踪剖析
nodes初始代码:
其中: var initialRadius = 10, initialAngle = Math.PI * (3 - Math.sqrt(5)); (大约是2.399963229728653)
nodes初始化之后的数据:
ps:观测发现对x,y的值进行简单的初始化。但是vx,vy的值都是空的。 ps:初始化的数据x,y只是半径和弧度(2.36)乘以了一个索引i而计算出来的数据。并没有充分考虑布局的特点。碰撞了重复了,离的太近了。
force中link.js执行之后。
ps: x,y的值都不变,vx,vy作出调整。
manyBody.js执行之后
ps: x,y的值仍然不变,只是vx,vy幅度变化了。
alpha的渐变过程
center.js执行之后
ps: x,y的值变化了,并且不在是scale而是具体的像素了,但是vx,vy没有改变。
第二次link.js执行之后
ps:发现x,y都在原来的vx,vy基础上做出了改变,并且又重新规划了vx,vy。说明在第二次Link.js之前x,y进行了重置。
关于center.js的简单阅读
function force() { var i, n = nodes.length, node, sx = 0, sy = 0;
for (i = 0; i < n; ++i) { node = nodes[i], sx += node.x, sy += node.y;}// 这里的x,y,代表中心的x,y 以像素为单位的,宽和高的一半,没有scale的概念for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) { node = nodes[i], node.x -= sx, node.y -= sy;}复制代码
} 总体解释通过第一个for循环,得到sx和sy的值,也就是x,y所有值的和。 sx = sx / n - x 表示平均的sx到x的距离 sy = sy / n - y 表示平均的sx到y的距离。 所有的节点的x,y都向这个靠拢就行了。