基于SVG利用力导向图对节点进行布局,容易导致布局失败的原因有两个:
1. 计算节点位置耗费大量的CPU,导致页面操作冻结;
2. 添加DOM节点到SVG元素时,渲染、重绘会耗费大量的GPU,导致页面直接崩溃;
针对以上的情况,可以采取如下策略:
1. 布局过程中,在页面只显示节点;
2. 布局过程中,在页面同时显示节点与连线;
3. 布局过程中不显示节点与连线,布局完成后显示;
4. 将布局算法放入Worker进行计算,同时显示节点与连线;
表一是各种策略的测试结果,每次布局都添加同样数量节点与连线,见下表。
表一:布局策略的完成时间(单位:毫秒)
节点数量\显示方式 | 只显示点 | 显示点与线 | 布局完成后显示 | Worker |
---|---|---|---|---|
1000 | 11301 | 17854 | 6309 | 8313 |
2000 | 17854 | 20886 | 6243 | - |
3000 | 32831 | 51262 | 8249 | - |
4000 | 43536 | 69135 | 10307 | 11395 |
10000 | 116512 | 181092 | 36966 | 48890 |
从上表可以看出,达到10000个节点与连线后,计算时间最短也需要37秒,基本上处于不可用状态。
在此过程中,还有如下一条数据,布局一万个节点至少需要18278 X 18336的面积,也就是说,至少需要40块1920 X 1080的显示屏,才能将图片完全显示,平均每个节点占据的面积为33514平方像素,约为183*183的大小,当然,这主要是由节点之间的连接关系决定的。
继续加大数据量进行测试,主要以Worker的方式:
节点数量\显示方式 | Worker布局完成后显示 | 布局时间 | 渲染时间 | 画布最小尺寸 | SVG文件大小 | 单位面积平方像素 |
---|---|---|---|---|---|---|
100000 | 771787 | 765841 | 5946 | 58118 * 58177 | 21.2m | 33776=183*183 |
200000 | 1643614 | 1630699 | 12915 | 82185*82141 | 42.5m | 33753=183*183 |
300000 | 36395188 | 36371453 | 23735 | 100708*100703 | 63.7m | 33805=183*183 |
300000个节点布局的示例如下:
示例代码
Worker计算的核心代码如下:
importScripts('d3.v4.min.js')var sim = d3.forceSimulation();// 每次数据发生变更时,通知布局进行DOM修正 var tickHandler = function() { postMessage({ nodes, links })}sim.on('tick.force', tickHandler)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
计算画布尺寸的代码如下:
// 计算左上角的点与右下角的点var max = {x : 0, y : 0}, min = {x : 0, y : 0}// 遍历所有的节点nodes.forEach(function(item) { if(item.x > max.x) { max.x = item.x } if(item.y > max.y) { max.y = item.y } if(item.x < min.x) { min.x = item.x } if(item.y < min.y) { min.y = item.y }})// 改进SVG的视图区域var width = max.x - min.x, height = max.y - min.y;// 设置大小svg.attr('width', width)svg.attr('height', height)// 设置可见区域svg.attr('viewBox', min.x + ',' + min.y + ',' + width + ',' + height)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
策略改进
通过上面的数据我们可以得出这样的结论:
1. 无论多大的显示屏都不可能把图谱显示完全;
2. 浏览器渲染节点容易导致浏览器崩溃;
于是我们也可以做出如下的改进策略:
1. 将布局结果保存到服务器;
2. 只显示可视区域之内的节点;
3. 通过可是区域的移动实现DOM节点的销毁;
4. 将SVG方式改为canvas方式,减少节点的消耗;
以Express为服务器,将布局方式放到服务器端,当节点数据生变化时,自动重排,并将结果保存到数据库,然后可视窗口滑动到哪里,就显示哪里的节点数据。
其他策略
- 按照连接关系对业务数据进行分片,例如分层、聚集等方法,然后依次布局;
- 采用多Worker方式;
- 采用静态布局方式,改用手动控制tick方法;
- 优化模拟器参数,尤其是alphaDecay、velocityDecay等核心参数。
总结
布局超大型的拓扑图或者图谱,需要多种策略的融合,无论是算法层面还是展现层面,都要经历长期的调试与优化,很难做到一蹴而就,没有最优的策略,但有最适合当前业务与环境的策略。