Antv G6动态更新自定义节点数据

背景

由于公司项目需求,最近研究了一下使用蚂蚁的antv G6来构建拓扑图。

成果实例

 

由于很多个性需求,所以图中的各种edge和node都是使用G6.registerNodeG6.registerEdge两个方法来自定义的。下面贴一下我自定义节点和边的代码片段。

     G6.registerNode(item.iconName, {
          draw(cfg, group) {
            if (item.iconName === 'warning1') {
              // 警报灯使用字体图标(制作闪烁动画)
              // 定义字体图标
              const keyShape = group.addShape('text', {
                attrs: {
                  x: 0,
                  y: 0,
                  fontFamily: 'iconfont', // 对应css里面的font-family: "iconfont";
                  textAlign: 'center',
                  textBaseline: 'middle',
                  text: 'ue60e', // 具体图标
                  fontSize: item.size,
                  fill: '#eaa153'
                },
                // must be assigned in G6 3.3 and later versions. it can be any value you want
                name: item.type
              })
              // 添加闪烁动画
              keyShape.animate(
                (ratio) => {
                  const color = ratio > 0.5 ? '#eaa153' : '#f83f3f'
                  return {
                    fill: color
                  }
                },
                {
                  repeat: true, // 动画重复
                  duration: 1000,
                  easing: 'easeLinear'
                }
              )

              // 强制刷新图标(默认情况刷新页面,图标会变成小方框)
              setTimeout(() => {
                keyShape.attr({})
              }, 0)

              return keyShape
            } else if (item.type === 9) {
              // 文本框
              const keyShape = group.addShape('text', {
                attrs: {
                  x: 0,
                  y: 0,
                  text: '文本框节点',
                  fontSize: item.size,
                  fill: '#FFF'
                },
                name: 'text-box'
              })
              return keyShape
            } else {
              // 普通图片节点
              const keyShape = group.addShape('image', {
                attrs: {
                  x: -(item.width / 2),
                  y: -(item.height / 2),
                  height: item.height,
                  width: item.width,
                  img: require(`@/assets/process_diagram_images/${item.iconName}.svg`)
                },
                // must be assigned in G6 3.3 and later versions. it can be any value you want
                name: item.type
              })
              // 给风扇添加转动动画
              if (item.type === 3) {
                keyShape.animate(
                  (ratio) => {
                    const toMatrix = G6.Util.transform(
                      [1, 0, 0, 0, 1, 0, 0, 0, 1],
                      [['r', ratio * Math.PI * 4]]
                    )
                    return {
                      matrix: toMatrix
                    }
                  },
                  {
                    repeat: true, // 动画重复
                    duration: 3000,
                    easing: 'easeLinear'
                  }
                )
              }
              return keyShape
            }
          },
          // 自定义选中状态
          setState(name, value, item) {
            const group = item.getContainer()
            const shape = group.get('children')[0]
            if (name === 'selected') {
              if (value) {
                shape.attr('shadowBlur', 6)
                shape.attr('shadowColor', '#00a5fc')
              } else {
                shape.attr('shadowBlur', 0)
                shape.attr('shadowColor', 'transparent')
              }
            }
          }
        }
        )
        G6.registerEdge(
          'pipeLine',
          {
            afterDraw(cfg, group) {
              const shape = group.get('children')[0]
              shape.attr('stroke', 'rgba(255,255,255,0.4)')
              // 添加管线白底
              const startPoint = shape.getPoint(0)
              const endPoint = shape.getPoint(1)
              group.addShape('path', {
                attrs: {
                  path: [
                    ['M', startPoint.x, startPoint.y],
                    ['L', endPoint.x, endPoint.y]
                  ],
                  stroke: 'rgb(229,229,229)',
                  lineWidth: 8
                },
                // must be assigned in G6 3.3 and later versions. it can be any value you want
                name: 'line-bg'
              })
              // 添加流动动画
              const flowPath = group.addShape('path', {
                attrs: {
                  path: [
                    ['M', startPoint.x, startPoint.y],
                    ['L', endPoint.x, endPoint.y]
                  ],
                  stroke: item.color,
                  lineWidth: 6
                },
                // must be assigned in G6 3.3 and later versions. it can be any value you want
                name: 'line-flow'
              })
              // 定义流向动画
              const lineDash = [10, 10, 10, 10]
              let index = 0
              flowPath.animate(
                () => {
                  index += 0.3
                  if (index > 40) {
                    index = 0
                  }
                  const res = {
                    lineDash,
                    lineDashOffset: -index
                  }
                  // returns the modified configurations here, lineDash and lineDashOffset here
                  return res
                },
                {
                  repeat: true, // whether executes the animation repeatly
                  duration: 3000 // the duration for executing once
                })
            },
            update: undefined
          },
          'line' // extend the built-in edge 'cubic'
        )

问题

根据需求,需要动态更新节点(node)和边(edge)的样式,比如需要动态修改一个文本节点的文本内容或者动态修改edge的颜色。在修改edge颜色的时候我使用的方法是通过graph.findById方法先查找到对应的节点,在通过点属性直接修改对应的属性值(最后不要忘了使用refreshItem函数重新渲染该元素)

 const el = this.graph.findById('edgeId')
  // 修改管道颜色(背景)
 el._cfg.model.style.stroke = 'rgba(99,99,99,0.77)'
  // 重新渲染
 this.graph.refreshItem(el)

但是,我想通过同样的方式修改文本框内容时候,却怎么也不生效

 const el = this.graph.findById('nodeId')
  // 修改文本内容
 el._cfg.keyshape.attrs.text = '我是变化后的文本'
  // 重新渲染
 this.graph.refreshItem(el)

最后通过反复阅读官方关于自定义节点部分的文档,终于发现了问题所在

换句话说,我在自定义edge时,使用的是afterDraw方法,并且继承了内置元素line的属性,所以在更新时他会执行line这个内置元素的update方法(具体实现请自行查阅)去修改我们edge数据,但是在我在自定义节点时使用的时使用的是draw方法,并且没有继承内置元素,所以在更新时,会重新执行draw这个方法,执行同样的方法,节点数据当然不会发生改变了。

解决方法

 在自定义节点时,需要自己去定义update这个方法,我的代码如下

        update(cfg, node) {
            // 若未指定registerNode的第三个参数并且未定义update方法时,则节点更新时会执行 draw 方法,所有图形清除重绘
            if (item.type === 9 && cfg.attrs) {
              // 定义更新文本节点的方法
              node.get('keyShape').attrs.text = cfg.attrs.text
              node.get('keyShape').attrs.fill = cfg.attrs.fill
              node.get('keyShape').attrs.font = `normal normal normal ${cfg.attrs.fontSize}px sans-serif`
              node.get('keyShape').attrs.fontSize = cfg.attrs.fontSize
            }
          },

最后在动态更新时,只需要使用graph.updateItem修改节点的数据即可

// 动态刷新文本框的节点的内容
    refreshTextBox(params) {
      const { id, text, fontSize, fill, variation } = params
      const node = this.graph.findById(id)
      // fontSize参数必须为数字
      this.graph.updateItem(node, {
        attrs: {
          text: text || '文本内容出错',
          fontSize: fontSize || 14,
          fill: fill || '#FFF',
          variation: variation || 'test'
        }
      })
    }

核心

解决方案的核心就是在自定义节点时使用update这个方法,另外大家在使用第三方插件时,遇到问题一定要去多阅读文档,答案或许就在文档中。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>