Antv G6的踩坑记录

1. 前言

前几天刚写下了一篇关于介绍G6的博客,有兴趣的能够看一下。后面回想起来发现当时把篇幅都花在介绍G6的核心概念上面了,真正实战和后面问题部分的总结还不够完善。所以再写一篇博客来补充一下。node

2. 实现上的一些坑

正如前篇提到的,在实现的时候遇到一些关于渲染性能和布局方面的问题。那就一步步来先从性能方面介绍吧。git

2.1. 性能问题

2.1.1. 首屏渲染

因为需求涉及大量数据的渲染,刚开始作demo的时候模拟了1000+的数据,测试一下G6树图布局的渲染性能。第一版的demo写好以后发现渲染效果挺差的,启动的时候竟然有9秒的白屏时间。而在官方提供的一个性能demo,节点数量50000+的状况下才出现交互上的卡顿,并且首屏的渲染加上网络的请求时间加起来也不过8s左右。因而打开了Performance找一下问题出在哪一个地方。github

    

从上面的性能截图发现,pathFinder这个方法耗时了6s多。重复调试发现这个函数平均耗时都在6到7s。那咱们的优化就要从这个函数入手了。这是Antv G6/edge包下的一个函数,说明咱们的耗时跟树图的边有关系。后面查阅github上的issue和官方文档找到了答案,当咱们的边类型设置为polyline时,默认会根据 A* 算法自动生成折线,而这个算法的时间复杂的挺高的。咱们尝试着切换一下edge的type看一下效果。总体的耗时在1s左右。算法

      

咱们经过两张gif更直观地感觉一下优化的效果。canvas

优化前:性能优化

优化后:
markdown

不过当咱们确实须要折线的样式又不想使用性能消耗比较大的polyline时,能够采用自定义edge的方式生成折线的样式, 这种方式的渲染表现基本与内置边一致。这里简略贴下自定义折线的核心代码。网络

G6.registerEdge(
        "h-poly-line",
        {
          draw(cfg, group) {
            const { startPoint, endPoint } = cfg;
            const shape = group.addShape("path", {
              attrs: {
                stroke: "#333",
                path: [
                  ["M", startPoint.x, startPoint.y],
                  ["L", endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y], // 三分之一处
                  ["L", endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y], // 三分之二处
                  ["L", endPoint.x, endPoint.y],
                ],
              },
              name: "h-poly-line",
            });
            return shape;
          },
          update: undefined //这里须要重写update否则默认继承line的方法
        },
        "line"
      );
复制代码

使用的时候只须要在实例化Graph的配置中指定edge的type便可。数据结构

const graph = new G6.Graph({
      ...
      defaultEdge: {
        type: 'h-poly-line' //指定边的类型
      }
      ...
    })
复制代码

官方还提供了另一种优化渲染的方案,就是经过 Web-Worker 机制去渲染,而不阻塞页面的其余部分用户交互。使用上也很简单一样是在config中配置便可。函数

2.1.2. 交互性能

一样是数据量1000+的状况,咱们的demo在拖拽和缩放的交互上面会出现卡顿。前面提到了官方的性能例子并无出现这个问题,主要是由于官方的例子中使用了内置的节点,因为要知足展现更多比赛信息的需求,demo则是使用了自定义的节点。这致使了咱们在交互的时候要花更多的时间去从新渲染整个节点。官方给这方面的状况提供了一个优化的方法, 代码以下。在节点交互的时候,不渲染除keyShape外的Shape。虽然带来更好的性能,可是开启这个功能后用户的视觉体验是会比较差的。因此使用这个方案还要考虑清楚。

const graph = new G6.Graph({
      ...
       modes: {
          default: [
            "drag-canvas",
            {
              type: "zoom-canvas",
              enableOptimize: true //开启性能优化
            }
          ],
        },
      ...
    })
复制代码

另外就是从node入手优化了,减小节点中没必要要的Shape,这个要根据实际状况分析,这里就不展开了。

2.2. 布局问题

另外一个比较难处理的问题就是布局问题,内置的树图布局并不能计算出节点准确的坐标,致使节点重叠在一块儿。下面是采用默认参数下的紧凑树图布局效果。

            

目前是经过主动去设定间隔和节点宽高去调整布局,但我的感受这种方法不是特别的优雅。配置的代码以下:

const graph = new G6.Graph({
      ...
      layout: {
          type: "compactBox",
          direction: direction,
         //下面四个属性配置布局中的节点宽高和层级间隔
         //官方文档中这几个属性的function|number
         //不过在4.3.4版本下使用number会报错
         //参数为节点信息
          getWidth: ({type, size}) => {
            if(type === "race-node"){
              return size[0]
            }
            return 200
          },
          getHeight: ({type, size}) => {
            if(type === "race-node"){
              return size[1]
            }
            return 100
          },
          getHGap: ({type}) => type === 'race-node'? 25 : 10,
          getVGap: ({type}) => type === 'race-node'? 25 : 100
        }
      ...
    })
复制代码

修改配置后的效果以下:

    

3. 使用上的一些坑

翻阅文档和实现过程当中发现,树图布局仍是有比较多的限制。树图布局并不支持放在子图布局中,也不支持Web-Worker优化。因为它跟通常图的数据结构不一致,二者不能在画板上混用。另外树图布局中也不能使用Combo(即将多个节点归为一组)。根据官方的说法,上面这几个功能在短期内也不打算支持。在某些状况咱们可能须要操做树图中某一层级的数据,因为没有Combo这就很难实现了,咱们只能用其余的布局去模拟树图,好比说用Darge布局去实现。
Darge布局不像树图同样能够根据数据结构生成节点关系这一点须要另外生成,其它的用法二者都比较相似,性能表现方面也比较相近,Darge布局基本上能够还原通常的树图布局。

// 紧凑树布局 规定的数据类型
const compactBoxData = [
    {
        id: '1',
        children: [
            {
                id: '2',
                children: []
            }
            ...
        ]
    }
]
// 加载数据
graph.data(compactBoxData)

//Darge布局
//须要将元数据转换成下列形式
const dargeData = {
    nodes: [
        {
            id: '1'
        },
        {
            id: '2'
        }
        ...
    ],
    edges: [
        {
            source: '1',
            target: '2'
        }
    ]
}

// 加载数据
graph.data(dargeData)
复制代码

完整的代码实现能够看官方的demo
另外还要吐槽一下,类JSX语法定义节点的方式使用体验上不如原来的addShapeAPI,并不能很好的描述Shape间的关系,嵌套层级过深的话会出现重叠等布局bug。

4. 总结

以上就是目前遇到的一些坑。虽然有些小的问题,但在社区主流可视化方案中,G6更加适合图可视化的场景,开箱即用同时又有较好的扩展性,提供的布局和交互能力基本能够覆盖常见的业务需求。须要快速开发的状况下G6确实是个不错的选择。

相关文章
相关标签/搜索