手摸手使用G6实现(轻)图编辑应用系列-初识G6

3,035 阅读3分钟

我正在参加「掘金·启航计划」

系列文章:

需求:对数据进行拓扑展示,简单的交互效果;支持编辑、查看节点间关系

简单效果展示

实现功能时,遇到许多问题,将官方文档、github翻阅了无数次;想着通过这个系列demo实现进行记录,也避免其他人重复犯错,节约时间

主要分两个区域:物料区、画布区;支持切换查看、编辑模式;编辑模式可从物料区拖拽物料,进行布局以及连线建立关系,建立关系时可指定箭头指向;无连线的节点可通过删除回到物料区;

graph.gif

框架比较

目前 AntV 有两个关于图的库:G6、X6(详细区别)

  • G6
    • 探索数据、获得洞察以及其他的辅助能力
    • 底层canvas、svg都支持(可选),大量节点优选
    • 节点内部图元复杂的优先
  • X6
    • 创建、编辑数据样式与形状
    • 底层基于svg,200以内节点的图编辑场景优选
    • 节点内部图元简单的优先

场景:节点数量较大,节点间线数量无限制;节点内图元实现时有十个,并且对于图编辑的需求比较轻,调研发现基于G6的扩展机制可满足需求,最终决定使用G6实现

实现过程

  • 数据处理
  • 自定义节点
  • 自定义线、箭头
  • 节点、线的交互
  • 自定义行为
  • 辅助插件使用
  • 后续优化

数据处理

  • G6支持的数据格式
<script>
  const graphData = {
    // 点集
    nodes: [
      {
        id: 'node1', // 节点的唯一标识
        x: 100, // 节点横坐标
        y: 200, // 节点纵坐标
        label: '起始点', // 节点文本
      },
      {
        id: 'node2',
        x: 300,
        y: 200,
        label: '目标点',
      },
    ],
    // 边集
    edges: [
      // 表示一条从 node1 节点连接到 node2 节点的边
      {
        source: 'node1', // 起始点 id
        target: 'node2', // 目标点 id
        label: '我是连线', // 边的文本
      },
    ],
  };
</script>
  • 利用样式优先级实现每个线不同的箭头指向(数据样式 > 全局图样式)
graphData.edges.forEach(item =>{
    item.style = {
      startArrow: item.direction === 'double' ? arrowStyle['startArrow'][item.type] : false,
      endArrow: item.direction === 'double' ? arrowStyle['endArrow'][item.type]: item.direction === 'single' ? arrowStyle['endArrow'][item.type]:false,
    }
})
  • 针对节点间线类型处理,使用G6.Util.processParallelEdges公共方法处理
    • 使用
    G6.Util.processParallelEdges(graphData.edges, 30, 'customQuadratic', 'customLine', 'customLoop');
    
    • 目的
      • 改变边数据上的类型,以及获取到对应类型相应的配置字段
    • 入参
      • 边数组
      • 平行边间的距离 - offsetDiff
      • 节点间存在多条边的边类型
      • 节点间存在一条边的边类型
      • 自环边的边类型
    • 处理过程
      • 将边数组转换为 key 为源-目标节点 val 为 edge 对象数组的 edgeMap
      • 找出节点间 startPoint、 endPoint 相反的 edge
      • 遍历 edgeMap
        • 节点间存在多条边,将指定边类型赋值到 edge 对象上;
          • 主要是计算边的控制点
            • curveOcffset 值(控制点距离两端点连线的距离)
            • curvePosition 弯曲的位置使用默认的0.5
          • 保证总边数奇数边,有一个边 curveOcffset 为 0,其余的边基于 offsetDiff * n * (k % 2 === 0 ? 1 : -1) 进行计算,此外与两端点连接的直线方向相反的需要 * -1
          • 总边数偶数边,除没有两端点连接的直线,其余的同上
        • 节点间存在一条边,将指定边类型赋值到 edge 对象上
        • 自环边,将指定边类型赋值到 edge 对象上;按照固定的 8 个方向划边,每超过8自环边,自环的曲度增加20
  • 创建图实例
graph = new G6.Graph({
    container,
    width,
    height,
    modes: {
      default: ['drag-canvas', 'zoom-canvas'],
    },
    // 默认节点样式
    defaultNode: styleObj.defaultNode,
    // 默认线样式
    defaultEdge: styleObj.defaultEdge,
});
  • 加载并渲染数据
graph.data(graphData);
graph.render();

结尾

截至到此,数据处理完毕,通过创建Graph实例可渲染出拓扑关系,下一节会介绍实现自定义节点的步骤

希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励