前端工程化七-前端可视化优化
# 万级节点优化
10w 条原始数据,经过去重、去无效点、预加工之后可得约 5w 个节点,以及 4w 多条连线。这些数据保存在一个 Object 里,数据格式如下,约占用 10M 内存。
{
"nodes": ["A", "B", "C", ...],
"links": [{
"source": "A",
"target": "B"
}, {
"source": "C",
"target": "D"
}, ...]
}
2
3
4
5
6
7
8
9
10
在 Three.js 中构造物体时,最常使用 THREE.Geometry
构造几何体。Geometry
是 Three.js 中的一种数据结构,其包含了几何体的顶点位置、颜色等等信息,储存信息时使用了 THREE.Vector3
, THREE.Color
等数据结构,读写非常直观方便,但是性能一般。按照最寻常的思路,对于每个节点,我们需要使用 THREE.CircleGeometry
构造一个圆,对于每条线,我们需要使用 THREE.Line
构造一条线。
// 最初版本
// 每个节点绘制一个圆
this.paintData.nodes.forEach((node) => {
node.geometry = new THREE.CircleGeometry(5, 12)
node.material = new THREE.MeshBasicMaterial({color: 0xAAAAAA})
node.circle = new THREE.Mesh(node.geometry, node.material)
this.scene.add(node.circle)
})
// 每条线绘制一个线段
this.paintData.links.forEach((link) => {
link.lineMaterial = new THREE.LineBasicMaterial({color: 0xAAAAAA})
link.lineGeometry = new THREE.Geometry()
link.line = new THREE.Line(link.lineGeometry, link.lineMaterial)
link.line.frustumCulled = false
this.scene.add(link.line)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 复杂度优化
原始数据总是要预处理的,比如统计最有影响力的(分享数最多)的节点,筛掉没有分享关系的无用节点,进行数据剪裁等等。海量数据情况下,使用合适的算法就很重要了;初版写的很随意,遍历套遍历,复杂度较高,1w 数据还能接受,跑个几百 ms 出来了,10w 数据直接卡住六七秒。后来优化,多用 hashmap,空间换时间,改写了两三版,最终将计算耗时控制在 2s 以内,还算理想。
# 多web worker拆分
将计算过程迁移到 worker 中可以避免阻塞主线程,保证交互的流畅;然而为了最大化加速计算,我们可以拆分至多个 web worker 中,以此充分利用多核性能。Javascript Web Workers Test v1.4.0 (opens new window) 是一个 web worker 测试,测试可知在多核机器上,拆分确实可以显著缩短计算时间。借助浏览器接口 navigator.hardwareConcurrency (opens new window) 我们可以获得处理器核心数,然后就可以拆分,比如 8 核机器拆出 7 个 worker 线程可以实现最大化利用核心。计算逻辑的拆分和结果的合并都需要自行设计,本文仅作了调研,由于计算耗时已经较短没有再做拆分工作。
# GPU加速
服务器上头像图片都是方形的,但是绘制时我们想要圆形图像,怎么处理出圆角效果呢?按通常思路,我们可以借助 canvas api,画个圆填充图片,最后导出新图片(见张鑫旭大大文章:小tip: SVG和Canvas分别实现图片圆角效果 (opens new window))。但由于我们具有操作片元着色器的能力,于是可以直接在着色器上进行纹理的修改 (opens new window),这里不但裁成了圆角,顺便还做了描边和抗锯齿。着色器直接运行在 GPU 上,性能很好。如果用软件模拟抗锯齿,开销肯定大得多。
# 节流
在节点数量庞大时,节点头像的拉取和绘制会成为一个性能问题,一般来说当视野范围很大时,节点很小,图片无需加载,可以设置只有在经过缩放,节点大于一定程度(即场景相机 Z 坐标小于一定值)时才加载视口内头像。『判断视野内有哪些节点并加载』这个操作若在每帧都执行频率太高了,可以使用 throttle 技术限制到每秒执行一次;同时头像物体缓存起来,视野移动时进行动态卸载与加载,避免头像加载过多带来性能问题。
https://juejin.cn/post/6844903709982326792#heading-6