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

Posted 恁说叫啥就叫啥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.fontSizepx 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这个方法,另外大家在使用第三方插件时,遇到问题一定要去多阅读文档,答案或许就在文档中。

以上是关于Antv G6动态更新自定义节点数据的主要内容,如果未能解决你的问题,请参考以下文章

React使用@antv/g6绘制树形图

AntV G6中动态数据提示框的实现

React下使用antv/g6实现树图/流程图

带你入门antv.g6流程图

React 中使用 AntV G6

g6 -TreeGraph案例:任务和任务流关系树图