流程图可视化和G6

1,219 阅读7分钟

G6是什么?

官方原文如下介绍:

G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、交互、动画等图可视化的基础能力。旨在让关系变得透明,简单。让用户获得关系数据的 Insight(领悟/了解/洞察力)。

在我的理解,G6它提供面向图形编程、开发者手动组装图形 & 自定义图形交互的能力,自己动手丰衣足食。

现在,我们loyalData技能点涉及到:

  • Graph图 在 G6 中

  • 图元素Item:节点/边/Combo

  • 图元素配置不同状态的样式

  • 基础动画

  • 响应事件

  • 拖拽生成边,将两个节点连起来

  • 删除节点、边

  • 获取图数据

Graph图

Graph 对象是图的载体,它包含了图上的所有元素(节点、边等),同时挂载了图的相关操作(如交互监听、元素操作、渲染等)。

Graph 对象的生命周期为:初始化 —> 加载数据 —> 渲染 —> 更新 —> 销毁。

图元素Item:节点/边/Combo

  1.  每个图元素由一个或多个 图形(Shape) 组成,会有自己的唯一关键图形(keyShape)
  • shape 指 G6 中的图形、形状,它可以是圆形、矩形、路径、图片等
  1. 实现一个自定义的节点渲染到画布上必须满足
  • 这个图元素有全局唯一的名字

  • 通过数据渲染图时,节点的 type 指定为图元素的名称

  •   // 自定义作业节点
      G6.registerNode('customNode',	{
            draw (cfg, group) {
              const keyShape = group.addShape()
              return keyShape // 每个节点都有一个keyShape, 所以这一步是必须的
            } 
       },
        'defaultNode')
      // 通过在数据中指定节点type,来渲染自定义的作业节点
      // 配置渲染的节点类型还有全局配置、函数配置等其他方式,请自行查阅
      const data = {
        nodes: [
          {
            id: 1,
            type: 'customNode'    }
        ]
      }
      G6.read(data)
    

节点Item

1、anchorPoints: 节点的连接点 anchorPoint 指的是边连入节点的相对位置

2、自定义节点

G6.registerNode(
  'nodeName',
  {
    /**
     * 绘制 combo,包含文本
     * @param  {Object} cfg combo 的配置项
     * @param  {G.Group} group 图形分组,combo 中的图形对象的容器
     * @return {G.Shape} 绘制的图形,通过 combo.get('keyShape') 可以获取到
     */
    draw(cfg, group) {},
    /**
     * 绘制后的附加操作,默认没有任何操作
     * @param  {Object} cfg combo 的配置项
     * @param  {G.Group} group 图形分组,combo 中的图形对象的容器
     */
    afterDraw(cfg, group) {},
    /**
     * 更新 combo ,combo 文本
     * @override
     * @param  {Object} cfg combo 的配置项
     * @param  {Combo} combo combo item
     */
    update(cfg, combo) {},
    /**
     * 更新 combo 后的操作,一般同 afterDraw 配合使用
     * @override
     * @param  {Object} cfg combo 的配置项
     * @param  {Combo} combo combo item
     */
    afterUpdate(cfg, combo) {},
    /**
     * 设置 combo 的状态,主要是交互状态,业务状态请在 draw 方法中实现
     * 单图形的 combo 仅考虑 selected、active 状态,有其他状态需求的用户自己复写这个方法
     * @param  {String} name 状态名称
     * @param  {Object} value 状态值
     * @param  {Combo} combo combo item
     */
    setState(name, value, combo) {},
    /**
     * 获取锚点(相关边的连入点)
     * @param  {Object} cfg combo 的配置项
     * @return {Array|null} 锚点(相关边的连入点)的数组,如果为 null,则没有锚点
     */
    getAnchorPoints(cfg) {},
  },
  'extendedComboName',
);

3、节点的连接点 anchorPoint

边Edge

sourceAnchor: 边的起始节点上的锚点的索引值

targetAnchor: 边的终止节点上的锚点的索引值

自定义边、自定义箭头

Combo节点分组

自定义combo

v3.5以前是Group。包含折叠、收起。内部节点的拖拽移动

图元素配置状态样式

在实际使用 G6 的开发过程中,有两种不同的场景需要配置状态样式:

  1. 业务数据字段的属性值不同,绘制的节点样式也不一样,比如节点是禁用还是可用状态;

  2. 不同的交互,节点绘制的效果不同,比如 鼠标hover 和 鼠标点击选中节点;

    // 第一种,根据数据绘制节点
    // 1.1 准备数据
    const data = {
      nodes: [
        {
          id: 1,
          type: 'job-node',
          name: '巡检监控'
          disabled: true 
        },
        {
          id: 2,
          type: 'job-node',
          name: '核心数仓',
          disabled: false 
        }
      ]
    }
    // 1.2 注册自定义节点时,根据业务数据动态绘制
    G6.registerNode('job-node',
    	{
          draw (cfg, group) {
            // 在绘制过程中业务数据属性会传递到cfg,所以在这里我们能拿到数据中的name/disabled等属性
            const { name, disabled } = cfg
            const keyShape = group.addShape()
            // group.addShape() 方法返回图形实例
            // 根据业务数据属性动态修改图形的样式
            keyShape.attr({
              cursor: disabled ? 'not-allowed' : 'default' // 如果禁用(diabled 为 true),悬浮时会显示禁用的手势
            })
            return keyShape
          }	
    	},
    	'single-node'
    )
    
    // 第二种,根据交互配置样式
    // 2.1 注册节点时,配置状态样式
    G6.registerNode('job-node',
    	{
          options: {
            stateStyles: {
              hover: {
                fill: '#ccc'
              },
              select: {
                fill: 'red'
              }
            }
          }
          draw (cfg, group) {
            const keyShape = group.addShape()
            return keyShape
          }	
    	},
      // 继承内置节点是必要的,内部节点封装处理了节点的 `options.stateStyles` 配置在不同状态下 keyshape 和 子shape 的样式
      // 也就意味着不用我们去复写 `setState` 以及它内部处理节点各shape的样式
    	'single-node'
    )
    // 2.2 交互过程中给节点添加状态
    // 鼠标划入节点,节点背景置灰
    graph.on('node:mouseenter', evt => {
      const node = evt.item
      graph.setItemState(node, 'hover', true)
    })
    // 鼠标划出节点,节点背景置为默认
    graph.on('node:mouseout', evt => {
      const node = evt.item
      graph.setItemState(node, 'hover', false)
    })
    // 点击节点,节点背景变红
    graph.on('node:click', evt => {
      const node = evt.item
      graph.setItemState(node, 'select', true)
    })
    

setState: 用于响应外部对元素状态的改变。当外部调用 graph.setItemState(item, state, value) 时,该函数作出相关响应。主要是交互状态,业务状态请在 draw() 方法中实现

节点响应事件

G6 提供监听画布、节点、边、组等多维度的事件注册机制,我们可以根据实际场景为不同的维度注册事件。

另外在 G6 中,事件响应的最小单元是节点 or 边,如果想针对节点内的某个图形做事件响应,可以通过监听节点/边的事件,然后进一步通过事件对象以及节点/边的相关api去判断。

比如只有点击节点的 close-icon-shape 后,才能从画布中删除该节点。

G6.registerNode('job-node',
	{
      draw (cfg, group) {
        const iconShape = group.addShape('image', {
          attrs: {
            url: closeIcon
          },
          // 这里name属性定义是必须的,后续可以通过事件对象拿到此处定义的图形名称
          name: 'close-icon-shape'
        })
        const keyShape = group.addShape()
        return keyShape
      }
	},
 	'single-node'
)

const data = {...}
const graph = new G6.Graph({...})
graph.on('node:click', evt => {
  // 节点
  const item = evt.item
  // shape图形
  const target = evt.target 
  // 通过get('name')方法获取图形定义时的名称
  if (target.get('name') === 'close-icon-shape') {
    // 只有点击 close-icon-shape 图形时,才会触发
    graph.removeItem(item)
  }
})

拖拽生成边,将两个节点连起来

拖拽生成边由一系列事件组合而成,也可以说是一组动作,因此注册某单个事件响应很难达到我们想要的效果、甚至会影响到别的交互。

所以G6提供了我们将一组动作封装到一起的能力,这里就涉及到了G6的 交互模式自定义交互

通过交互模式的切换,我们就可以让图中元素对于同一个事件做出不同的响应。

例如,存在 default 和 edit 两种 mode(交互模式):

  • default 模式中包含点击选中节点行为和拖拽画布行为;
  • edit 模式中包含点击节点弹出编辑框行为和拖拽节点行为。

具体的效果可以参见G6图表示例中的 切换模式增加点和边

获取图数据

经过上述步骤,我们流程图基本实现了:

  • 节点的绘制和生成
  • 作业节点之间的流程依赖
  • 新增、删除节点

最后需要获取图数据并将其传给服务端将图数据持久化,在G6图中数据的获取也是相当简单,只需要通过下面的方法即可:

console.log(graph.save()) // graph 是 G6.Graph 的实例


// 上面的方法会打印出如下格式的对象
{
  nodes: [...],
  edges: [...],
  // 还有group/combo等组数据...
}

最后的最后

最后为各位看官奉上我在学习G6时做的笔记:

  • 自定义节点时的Shape图形:

所有图形相对于节点中心绘制(节点中心的坐标相对于画布,由group所在矩阵控制)。x / y 是指定 相对于节点中心 的位置的距离。尺寸属性用:width / height / r / rx / ry

  • groupshape 是图形层面上的概念,就类似 svg 的g标签和其他图形标签circle之类的;item 不是图形上的概念,作为画布上节点、边的实例,它包含可视化相关API的操作方法以及group的代理