vue 使用antv X6 绘制流程图 (二)

4,800 阅读3分钟

快点更新下一个,虽然只是小计.但是感觉会越拖越懒得写.

2022/7/4 更新,更新保存数据出现的bug,解决办法就是修改在clickNode里面在嵌套多一层customAttrs字段 this.clickNode.customAttrs

image.png

先创建画板

  // 初始化画布
  graph = new Graph({
    container: document.getElementById('orgAndPersonnelContainer'),
    grid: true,
    width: this.onresizeWidth, // 这个是自己定义的宽度
    height: this.onresizeHeight, // 这个是自己定义的高度
    background: {
      color: '#fff', // 设置画布背景颜色
    },
    mousewheel: {
      enabled: true,
      zoomAtMousePosition: true,
      modifiers: 'ctrl',
      minScale: 0.5,
      maxScale: 3,
    },
    panning: { 
      enabled: true, // 单独开启拖动操作
      modifiers: 'shift', // 按下shift才可以拖动
    },
    connecting: {
      router: {
        name: 'manhattan',
        args: {
          padding: 1,
        },
      },
      connector: {
        name: 'rounded',
        args: {
          radius: 8,
        },
      },
      anchor: 'center',
      connectionPoint: 'anchor',
      allowBlank: false,
      snap: {
        radius: 20,
      },
      createEdge() {
        return new Shape.Edge({
          attrs: {
            line: {
              stroke: '#A2B1C3',
              strokeWidth: 2,
              targetMarker: {
                name: 'block',
                width: 12,
                height: 8,
              },
            },
          },
          zIndex: 0,
        })
      },
      validateConnection({ targetMagnet }) {
        return !!targetMagnet
      },
    },
    highlighting: {
      magnetAdsorbed: {
        name: 'stroke',
        args: {
          attrs: {
            fill: '#5F95FF',
            stroke: '#5F95FF',
          },
        },
      },
    },
    resizing: true,
    rotating: true,
    selecting: {
      enabled: true,
      rubberband: true,
      showNodeSelectionBox: true,
    },
    snapline: true,
    keyboard: true,
    clipboard: true,
  })

接下来是拖动放大之类的一些事件,官方例子有写,我也是直接copy

这个真的是copy的没什么好说的QAQ 删除按钮的也在这里面

  快捷键与事件
  // copy cut paste
  graph.bindKey(['meta+c', 'ctrl+c'], () => {
    const cells = graph.getSelectedCells()
    if (cells.length) {
      graph.copy(cells)
    }
    return false
  })
  
  graph.bindKey(['meta+x', 'ctrl+x'], () => {
    const cells = graph.getSelectedCells()
    if (cells.length) {
      graph.cut(cells)
    }
    return false
  })
  graph.bindKey(['meta+v', 'ctrl+v'], () => {
    if (!graph.isClipboardEmpty()) {
      const cells = graph.paste({ offset: 32 })
      graph.cleanSelection()
      graph.select(cells)
    }
    return false
  })

  //undo redo
  graph.bindKey(['meta+z', 'ctrl+z'], () => {
    if (graph.history.canUndo()) {
      graph.history.undo()
    }
    return false
  })
  graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
    if (graph.history.canRedo()) {
      graph.history.redo()
    }
    return false
  })

  // select all
  graph.bindKey(['meta+shift+a', 'ctrl+shift+a'], () => {
    const nodes = graph.getNodes()
    if (nodes) {
      graph.select(nodes)
    }
  })

  //delete
  graph.bindKey('backspace', () => {
    const cells = graph.getSelectedCells()
    // console.log(cells.isEdge())
    // if (cells.length) {
    //   graph.removeCells(cells)
    // }
  })

  // zoom
  graph.bindKey(['ctrl+1', 'meta+1'], () => {
    const zoom = graph.zoom()
    if (zoom < 1.5) {
      graph.zoom(0.1)
    }
  })
  graph.bindKey(['ctrl+2', 'meta+2'], () => {
    const zoom = graph.zoom()
    if (zoom > 0.5) {
      graph.zoom(-0.1)
    }
  })

  // 调整节点大小事件
  graph.on('node:resized', ({ e, x, y, node, view }) => {
    this.setClickNode(node) // 这里是将节点保存到变量的方法
  })

  // 移动节点事件
  graph.on('node:moved', ({ e, x, y, node, view }) => {
  })

  // 点击背景板
  graph.on('blank:click', ({ e, x, y }) => { 
  })

  // 点击节点
  graph.on('node:click', ({ e, x, y, node, view }) => {
  })
  
  // 添加边事件, 这里是添加线的删除事件
  graph.on('edge:added', ({ edge, index, options }) => {
    edge.addTools([
      {
        name: 'button-remove', // 添加删除按钮
        args: {
          distance: '85%',
          attrs: {
            y: -50,
            width: 20,
            height: 20
          }
        },
      }, 
      // {
      //   name: 'boundary',
      //   args: {
      //     distance: 10,
      //   },
      // }
    ])
  })
  
  // 拖动新增的事件
  graph.on('node:added', ({ node }) => {
    node.addTools([
      {
        name: 'button-remove',
        args: {
          distance: '85%',
          y: -10, // 删除按钮的位置
          x: '100%',
        },
      }, 
      {
        name: 'boundary',
        args: {
          // distance: 20,
          padding: 15,
        },
      },
    ])

好像没什么好写的了= =

image.png 写下修改样式的吧

this.clickNode 是上图样式属性相关值的对象

this.clickNodeTemp 的指向是点击的节点( 对象赋值的话是指向同一个堆哦)

this.timeEnd 一次性定时器

this.time 定时器

  clickNode: {
    // 这里对象在嵌套一层,不然保存数据的时候会出现奇怪的bug,往下看
    customAttrs: {
        clickNodeContent: '', // 内容
        clickNodeContentSize: '', // 字体大小
        clickNodeBoxSizeWidth: '', // 盒子宽度
        clickNodeBoxSizeHeight: '', // 盒子高度
        clickNodeBgColor: '', // 背景颜色
        clickNodeTextColor: '', // 文字颜色
        clickNodeBorderColor: '', // 边框颜色
        positionx: 0, // x位置
        positiony: 0 // y位置
   },
}

html

  <el-button 
    @click="addOrSubOrColorClickNodeData('add', 'clickNodeBoxSizeWidth')" 
    size="mini" 
    icon="el-icon-caret-top" 
    class="mini-icon"
    @mousedown.native="holdDown('add', 'clickNodeBoxSizeWidth')" 
    @mouseup.native="holdUp()"
  ></el-button>

js

    // 设置属性值
    setClickNode(node) {
      this.clickNodeTemp = node // 指向节点
      this.clickNode.customAttrs.clickNodeContent = node.attrs.text.text
      this.clickNode.customAttrs.clickNodeContentSize = node.attrs.text.fontSize
      this.clickNode.customAttrs.clickNodeTextColor = node.attrs.text.fill
      const propTemp = this.clickNodeTemp.getProp() // 获取样式信息
      this.clickNode.customAttrs.clickNodeBoxSizeWidth = propTemp.size.width
      this.clickNode.customAttrs.clickNodeBoxSizeHeight = propTemp.size.height
      this.clickNode.customAttrs.clickNodeBorderColor = node.attrs.body.stroke
      this.clickNode.customAttrs.clickNodeBgColor = node.attrs.body.fill

      const relativePos = node.position({relative: true}) // 获取位置信息
      this.clickNode.customAttrs.positionx = relativePos.x
      this.clickNode.customAttrs.positiony = relativePos.y
    },
    // 鼠标按下时触发
    holdDown(addOrSubOrColorTemp, data) {
    var i = 0; //变量i
    this.timeEnd = setTimeout(() => {
      this.time = setInterval(() => {  //setInterval可一直执行内部函数
        this.addOrSubOrColorClickNodeData(addOrSubOrColorTemp, data);
        i++;  //若过一秒,执行一次i++
      }, 200);
    }, 800)

    if (i == 0) {  //i=0时证明无长按事件为单击事件
      // this.addOrSubOrColorClickNodeData(addOrSubOrColorTemp, data);
    }
    },

    holdUp() {
    clearTimeout(this.timeEnd)
    clearInterval(this.time); //如果按下时间不到1000毫秒便弹起,
    },
    
    // 加一减一或者颜色修改 addOrSubOrColorTemp 判断是点击加还是减  或者是选择颜色的
    addOrSubOrColorClickNodeData(addOrSubOrColorTemp, data) {
      if (addOrSubOrColorTemp == 'add') {
        this.clickNode[data]++
      } else if (addOrSubOrColorTemp == 'sub'){
        this.clickNode[data]--
      }
      this.clickNode.customAttrs.positionx = this.clickNode.customAttrs.positionx*1,
      this.clickNode.customAttrs.positiony = this.clickNode.customAttrs.positiony*1
      this.clickNodeTemp.updateAttrs(this.clickNode)
        if (this.clickNodeTemp.setAttrs) {
        // 用这个方法设置颜色样式
        this.clickNodeTemp.setAttrs(
          {
            body: { fill: this.clickNode.customAttrs.clickNodeBgColor, stroke: this.clickNode.customAttrs.clickNodeBorderColor },
            label: { 
              fill: this.clickNode.customAttrs.clickNodeTextColor 
            },
            text: {
              text: this.clickNode.customAttrs.clickNodeContent,
              fontSize: this.clickNode.customAttrs.clickNodeContentSize
            },
          }
        )

        if (addOrSubOrColorTemp && addOrSubOrColorTemp !== 'color') {
          // 用这个方法修改长宽, 位置
          this.clickNodeTemp.setProp({
            size: {
              width: this.clickNode.customAttrs.clickNodeBoxSizeWidth, 
              height: this.clickNode.customAttrs.clickNodeBoxSizeHeight, 
            },
            position: {
              x: this.clickNode.customAttrs.positionx*1,
              y: this.clickNode.customAttrs.positiony*1
            }
          })
        }
      }
    },

差点忘记一个最重要的东西,就是把数据转换的

    // 将页面的图形转换成json
    let preservationDataTemp = graph.toJSON() 
    
    // 将图形json绘制到页面,需要注意基本的宽高,
    // 网格配置不能有改变(暂时还没研究太深入,简单说就是配置不变就行,不然有些样式缺失就绘制不出来)
    graph.fromJSON(preservationDataTemp) 

不知道为什么 this.clickNode 会出现划红线的情况 1375466142ec44d24aaae14680a3c4c.png

所以我就试试多加个对象解决问题 this.clickNode.customAttrs 8a4abf1c74a1e668cfedbbd70678afb.png

好了 结束,比较枯燥.但是基本直接copy 能用.冲冲冲