图形化引擎antv/G6--自定义节点创建紧凑树

1.参考: G6 TreeGraph树图默认展开几层节点

graph.data(data)
        graph.render()
        graph.fitCenter() // 移到图中心
        setTimeout(() => {
          // 1 默认展开两层节点,之后,重新渲染
          G6.Util.traverseTree(data, function(item) {
            console.log(item)
            if (item.depth > 0) {
              //collapsed为true时默认收起
              item.collapsed = true
            }
          })
          graph.render()
          graph.fitCenter() // 移到图中心
        }, 10)

2.树形节点增删改,需要重新调用  graph.render()或者 graph.layout()

3.树形图,右键菜单插件

 

 

以此为例,右键菜单的触发事件回调函数:  handleMenuClick

const menu = new G6.Menu({
          offsetX: 10,
          offsetY: 10,
          itemTypes: ['node'],
          getContent(e) {
            const {item} = e
            console.log(item.getModel())
            let {bqglParentUuid} = item.getModel()

            if (bqglParentUuid == 'root') {
              return ``
            }
            const outDiv = document.createElement('div')
            outDiv.innerHTML = `<ul class="ant-cascader-menu">
              <li class="add">增加节点</li>
              <li class="addClassification">增加分类</li>
              <li class="del">删除节点</li>
              <li class="proview">查看节点</li>
              <li class="edit">编辑节点</li>
            </ul>`
            return outDiv
          },
          handleMenuClick: (target, item) => {
            var nodeId = item.get('id')

            const newParentData = graph.findDataById(nodeId)
            let newchildren = newParentData.children
            switch (target.className) {
              case 'add':
                var childData = {
                  id: 'child-data-' + new Date().getTime(),
                  type: 'flow-rect',
                  name: 'x-'
                }
                graph.addChild(childData, nodeId)
                graph.render()
                graph.fitCenter() // 移到图中心
                graph.fitView()

                break
              case 'addClassification':
                console.log(target.innerText)
                break
              case 'del':
                graph.removeChild(nodeId)
                graph.render()
                graph.fitCenter() // 移到图中心
                graph.fitView()
                // graph.fitView()
                break
              case 'proview':
                console.log(target.innerText)
                break
              case 'edit':
                console.log(target.innerText)
                break
              default:
                break
            }
          }
        })

4.树形图  矩形上线连接点的位置

 

 

//自定义节点
 G6.registerNode(
          'flow-rect',
    {
        getAnchorPoints() {
              //矩形上线连接点的位置
              return [
                [0.5, 0],
                [0.5, 1]
              ]
            }
     },
      'rect'//连接线
        G6.registerEdge('line-arrow', {
          options: {
            style: {
              stroke: '#ccc'
            }
          },
          draw: function draw(cfg, group) {
            const startPoint = cfg.startPoint
            const endPoint = cfg.endPoint

            const stroke = (cfg.style && cfg.style.stroke) || this.options.style.stroke
            const startArrow = (cfg.style && cfg.style.startArrow) || undefined
            const endArrow = (cfg.style && cfg.style.endArrow) || undefined
            //连接线路径
            const keyShape = group.addShape('path', {
              attrs: {
                path: [
                  ['M', startPoint.x, startPoint.y],
                  ['L', startPoint.x, (startPoint.y + endPoint.y) / 2],
                  ['L', endPoint.x, (startPoint.y + endPoint.y) / 2],
                  ['L', endPoint.x, endPoint.y]
                ],
                stroke,
                lineWidth: 1,
                startArrow,
                endArrow
              },
              className: 'edge-shape',
              name: 'edge-shape'
            })

            return keyShape
          }
        })

5. 点击切换选中

 

 


          setState(name, value, item) {
              var group = item.getContainer()
              // 获取外边框
              var nodeRect = group.find(function(e) {
                return e.get('name') === 'node-rect'
              })
              // 获取任务名称
              var collapseTitleText = group.find(function(e) {
                return e.get('name') === 'node-name'
              })
              // 获取折叠点
              var collapseText = group.find(function(e) {
                return e.get('name') === 'collapse-text'
              })

           
              // 设置状态
              if (name === 'selected') {
                if (value) {
                  nodeRect.attr({
                    stroke: '#1258ED',
                    lineWidth: 2
                  })
                  collapseTitleText.attr({
                    fill: '#1258ED'
                  })
                } else {
                  nodeRect.attr({
                    stroke: '#999999',
                    lineWidth: 1
                  })
                  collapseTitleText.attr({
                    fill: '#333'
                  })
                }
              }
        }

//
点击节点文字 graph.on('node-name:click', e => { const {item} = e graph.setItemState(item, 'selected', !item.hasState('selected')) // 切换选中 })

 

6.解决自定义节点动态更新调用graph.render(),坐标问题

根据G6坐标系的 pointX/pointY  缩放平移不影响  pointX/pointY  坐标计算   ,

通过记录最初创建时的 画布中心point坐标,计算拖拽后和最初创建的坐标差,在每次render后进行平移

记录缩放比例,render之后进行还原

注意:缩放比例和缩放中心的坐标,需要和拖拽后画布中心的坐标一致,否则会有位置差


//this.initGraphCenterPoint 为创建时保存的画布中心坐标
   updateGraph() {
      this.graphCenterPoint = graph.getGraphCenterPoint()
      this.graphCenterZoom = graph.getZoom()
      let x = this.graphCenterPoint.x - this.initGraphCenterPoint.x,
        y = this.graphCenterPoint.y - this.initGraphCenterPoint.y
        graph.render()
        graph.fitCenter()
        graph.translate(x, y)
        graph.zoomTo(this.graphCenterZoom, this.graphCenterPoint)
    },

7.双击节点编辑

   //监听双击点击 
graph.on('node:dblclick', e => {
        const nodeItem = e.item // 获取被点击的节点元素对象
        that.editNode(e)
        console.log('双击', nodeItem._cfg)
      })


//双击编辑
    editNode(evt) {
      let that = this
      const item = evt.item
      const model = item.get('model')
      const {x, y, width, height} = item.calculateBBox()
      const graph = evt.currentTarget
      const realPosition = evt.currentTarget.getClientByPoint(x, y)
      const el = document.createElement('div')

      el.style.fontSize = 16 + 'px'
      el.style.position = 'fixed'
      el.style.top = realPosition.y + 'px'
      el.style.left =
        realPosition.x + ((width - G6.Util.getTextSize(model.bqglLabel, 20)[0]) * graph.getZoom()) / 2 + 'px'
      el.style.transformOrigin = 'top left'
      el.style.transform = `scale(${evt.currentTarget.getZoom()})`
      const input = document.createElement('input')
      input.value = model.bqglLabel
      input.style.width = G6.Util.getTextSize(model.bqglLabel, 20)[0] + 'px'
      input.style.fontWeight = 'bold'
      input.style.height = height + 'px'
      input.className = 'dice-input'
      el.className = 'dice-input'
      el.appendChild(input)
      document.body.appendChild(el) 
      const destroyEl = () => {
        document.body.removeChild(el)
      }
      const clickEvt = event => {
        if (
          !(
            event.target &&
            event.target.classList.length &&
            event.target.className &&
            event.target.className.includes('dice-input')
          )
        ) {
          window.removeEventListener('mousedown', clickEvt)
          window.removeEventListener('scroll', clickEvt)

          graph.updateItem(item, {
            bqglLabel: input.value
          })
          //更新视图
          that.updateGraph()

          graph.layout()
          graph.off('wheelZoom', clickEvt)
          destroyEl()
        }
      }
      graph.on('wheelZoom', clickEvt)
      window.addEventListener('mousedown', clickEvt)
      window.addEventListener('scroll', clickEvt)
      input.addEventListener('keyup', event => {
        if (event.key === 'Enter') {
          clickEvt({
            target: {}
          })
        }
      })
    },

 

8.dom节点插入

SVG 与 DOM 图形在 V3.3.x 中不支持。 仅在 Graph 的 renderer 为 'svg' 时可以使用 DOM 自定义节点。

new G6.TreeGraph({
  renderer: 'svg',
})

 G6 的节点/边事件不支持 DOM 类型的图形。如果需要为 DOM 节点绑定事件,请使用原生 DOM 事件

参考文档:使用-dom-自定义节点

rectConfig 为基础公共变量配置对象

 rectConfig: {
        //配置项
        width: 200,
        height: 40,
        lineWidth: 1,
        fontSize: 16,
        fill: '#fff', // 填充背景
        radius: 4,
        stroke: '#F7F7F7',
        opacity: 1
      }
此处代码是循环插入
      cfg.list.forEach((item, i) => {
                  let agrments = `${i},"${cfg.id}","${item.bqglLabel}"`,
                    classList = item.selected ? `childNode selected` : `childNode`
                  group.addShape('dom', {
                    itemData: {
                      bqglUuid: cfg.bqglUuid,
                      bqglType: cfg.bqglType,
                      bqglLabel: cfg.bqglLabel
                    },
                    attrs: {
                      x: nodeOrigin.x + 10,
                      y: i * (rectConfig.height + 5) + nodeOrigin.y + 30,
                      width: rectConfig.width - 20,
                      height: rectConfig.height,
                      html: `
                      <div class='${classList}'
                        oncontextmenu='nodeChildern(event , ${agrments})'
                        onclick='clickNodeChildern(event , ${agrments})'
                        onmousemove='nodeChildernMοusemove(event, ${agrments})'
                        onmouseout='nodeChildernMοuseout(event, ${agrments})'
                        style='height:${rectConfig.height}px'
                      >
                        <span
                          ondblclick='dbclickNodeChildern(event , ${agrments})'
                        >${that.fittingString(item.bqglLabel, rectBBox.width - 10 * 2 - rectConfig.height, 16)}
                        </span>
                      </div>`
                    },
                    name: 'node-childern-rect'
                  })
                })
原生DOM事件函数,在 mounted 中声明 绑定在window上
//标签右键触发的事件
      window.nodeChildern = (e, index, parentId) => {
        
      }
      //标签点击触发的事件
      window.clickNodeChildern = (e, index, parentId) => {
      
      }
      //标签双击触发的事件
      window.dbclickNodeChildern = (e, index, parentId) => {
     
      }
    //鼠标移入
      window.nodeChildernMοusemove = (e, index, parentId, bqglLabel) => {
       
      }
      //标签鼠标移出
      window.nodeChildernMοuseout = (e, index, parentId, bqglLabel) => {
        t.style.display = 'none'
      }
 

9.节点偏移量修正

宽度未自适应,造成重叠的bug

 

 

在 getHeight 中改变下高度间距,触发了引擎  layout 重新渲染最终位置被修正
new G6.TreeGraph({
     layout: {
            type: 'compactBox', //紧凑型树形菜单
            direction: 'TB', //垂直
            dropCap: false,
            getHeight: nodeItem => {
          //添加判断后就可以自适应了
              if (nodeItem.collapsed) {
                return rectConfig.height + 30
              }
              return rectConfig.height + 20
            },
            getWidth: () => {
              return rectConfig.width + 40
            }
          },
})    

 

 

 

 

。。。

posted @ 2022-03-17 15:50  混名汪小星  阅读(8600)  评论(3编辑  收藏  举报