d3.js 关于力引导图的简单解析

代码操作一共两处
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);
复制代码
效果图

布局的一些基本共识

通常我们通常可以通过以下标准来衡量一个图布局算法的好坏:

  1. 交叉的边要尽量少
  2. 边长要尽量均衡
  3. 布局要尽量对称
  4. 单位面积能放尽量多的结点

以上标准前面的更重要,而且基本上是满足一些就会减弱另一些,而力引导算法基本上能很好的平衡,所以它算是一个很好的图布局算法。


本文来自 wry2008wry 的CSDN 博客 ,全文地址请点击:blog.csdn.net/wry2008wry/…

测试数据
{
    "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等方法。

(2) 代码 .force("center", d3.forceCenter(width / 2, height / 2)) 这个操作 a.把link中的source和target的指针指向了nodes中具体的值。 b.给中心坐标x,y赋值, c.把center.js中的force的这个里放到forces中, d. center.js中的initialize进行初始化,给nodes节点赋值
(3) 代码 simulation.nodes(graph.nodes) 调用nodes方法对数据进行处理。

(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都向这个靠拢就行了。

d3.js 关于力引导图的link.js的简单阅读

d3.js 关于力引导图的link.js的简单阅读

d3.js 关于力引导图的manyBody.js的简单阅读

d3.js 关于力引导图的manyBody.js的简单阅读

完整代码

码云代码地址:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值