最近公司需要做一个流程图, 看了看antv/X6感觉挺合适,就研究了半个月。 网上也没什么资料,又怕自己忘,就自己记录一下用到得一些事件方法,方便以后再用到可以查阅。

一:实现流程图最重要得就是画布了,官网上都有文档可以轻松实现画布。下面放一下我用VUE写得画布代码。

首先最重要得就是下载依赖了, x6在vue中下载得话需要下载两个依赖;

npm install @antv/x6 --save
npm install @antv/x6-vue-shape
把这两个依赖下载好就可以在vue中使用X6了  


首先在页面引入依赖
import "@antv/x6-vue-shape";
import { Graph } from '@antv/x6';

下面开始写代码
在methods里定义一个initX6方法, 然后拿到mounted里调用

initX6() {
      this.graph = new Graph({
        container: document.getElementById("containerChart"), //这是画布需要挂载得ID名字
        width:1000,  //画布宽度
        height: 500, //画布高度
        grid: this.grid, //画布样式, 在modou层自己定义用this调用
        autoResize: true, //跟随窗口自动变化大小
      }); 
    },

需要定义画布背景网格得话可以自己定义, 下面是我自己定义得一个背景网格,直接在grid后面使用this.名字调用就可以。

data(){
    return{
        grid: {
            // 网格设置
            size: 20, // 网格大小 10px
            visible: true, // 渲染网格背景
            type: "mesh",
            args: {
              color: "#D0D0D0",
              thickness: 1, // 网格线宽度/网格点大小
              factor: 10,
            },
          },
        }
    }

这个时候在css里给你的挂载容器定义一个宽高你就可以在页面上看到一个画布了。 给他调整一个位置就开始写节点吧。

新建一个JS文件, 用来定义节点和写拖拽代码。

import '@antv/x6-vue-shape';
import { Addon} from '@antv/x6';
// 拖拽生成四边形
export const startDragToGraph  = (graph,type,e) =>{
    const node = 
    graph.createNode({
    width: 100,  //节点的宽度
    height: 60,   //节点的高度
    attrs: {
      label: {
        text: type,    
        fill: '#000000',
        fontSize: 14,
        textWrap: {
          width: -10 ,
          height: -10,
          ellipsis: true,
        },
      },
      body: {
        stroke: '#000000',
        strokeWidth: 1,
        fill: '#ffffff',
      }
    },
    ports: ports
  })
    const dnd = new Addon.Dnd({target:graph})
    dnd.start(node,e)
}
//下面是锚点的代码。 知道就行了 我就不一一写了。
const ports = {
    groups: {
      // 输入链接桩群组定义
      top: {
        position: 'top',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#2D8CF0',
            strokeWidth: 2,
            fill: '#fff',
          },
        },
      },
      // 输出链接桩群组定义
      bottom: {
        position: 'bottom',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#2D8CF0',
            strokeWidth: 2,
            fill: '#fff',
          },
        },
      },
      left: {
        position: 'left',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#2D8CF0',
            strokeWidth: 2,
            fill: '#fff',
          },
        },
      },
      right: {
        position: 'right',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#2D8CF0',
            strokeWidth: 2,
            fill: '#fff',
          },
        },
      },
    },
    items: [
      {
        id: 'port1',
        group: 'top',
      },
      {
        id: 'port2',
        group: 'bottom',
      },
      {
        id: 'port3',
        group: 'left',
      },
      {
        id: 'port4',
        group: 'right',
      }
    ],
}

定义完节点和锚点以后回到vue页面引入JS文件

import { startDragToGraph } from "../methods.js"; //引入定义好的JS文件


去HTML层加入手动拖拽的节点内容。
<template>
  <div class="warp">
    <div id="containerchart"></div>
    <div
      v-for="(item, index) in List"
      :key="index"
      class="btn"
      :title="item"
      @mousedown="startDrag(item, $event)"
    >
      <span>
        {{ item }}
      </span>
    </div>
  </div>
</template>

//循环的list是自己写的
data() {
    return {
      List: ["内置节点"],
      grid: {
        // 网格设置
        size: 20, // 网格大小 10px
        visible: true, // 渲染网格背景
        type: "mesh",
        args: {
          color: "#D0D0D0",
          thickness: 1, // 网格线宽度/网格点大小
          factor: 10,
        },
      },
    };
  },

然后在methods里定义方法,把type传到js文件里。 

 startDrag(type, e) {
      startDragToGraph(this.graph, type, e);
    },


完成以上你就可以得到如下效果,从右侧拖拽节点到画布上就可以展示了。

 只不过现在拖拽和线条还是有问题的。  接下来解决一下连线的问题。

 data() {
    return {
      List: ["内置节点"],

      
      grid: {
        // 网格设置
        size: 20, // 网格大小 10px
        visible: true, // 渲染网格背景
        type: "mesh",
        args: {
          color: "#D0D0D0",
          thickness: 1, // 网格线宽度/网格点大小
          factor: 10,
        },
      },


      connectEdgeType: {
        //连线方式
        connector: "normal",
        router: {
          name: "",
        },
      },

       //首先在data里定义下面三个内容,下面需要用到
      graph: "",
      type: "grid",
      selectCell: "",
    };
  },

//然后在methods里的initX6方法里加上下面的方法
         connecting: {
          // 节点连接
          anchor: "center",
          connectionPoint: "anchor",
          allowBlank: false,
          snap: true,
        },
         createEdge() {
            return new Shape.Edge({
              attrs: {
                line: {
                  stroke: "#1890ff",
                  strokeWidth: 1,
                  targetMarker: {
                    name: "classic",
                    size: 8,
                  },
                  strokeDasharray: 0, //虚线
                  style: {
                    animation: "ant-line 30s infinite linear",
                  },
                },
              },
              label: {
                text: "",
              },
              connector: _that.connectEdgeType.connector,
              router: {
                name: _that.connectEdgeType.router.name || "",
              },
              zIndex: 0,
            });
          },

//注意  这两个方法写到new的Graph实例里面。

//然后给节点写上拖拽事件,这个方法写到initX6方法里,new的Graph实例外。
   this.graph.on("selection:changed", (args) => {
        args.added.forEach((cell) => {
          this.selectCell = cell;
          if (cell.isEdge()) {
            cell.isEdge() && cell.attr("line/strokeDasharray", 5); //虚线蚂蚁线
            cell.addTools([
              {
                name: "vertices",
                args: {
                  padding: 4,
                  attrs: {
                    strokeWidth: 0.1,
                    stroke: "#2d8cf0",
                    fill: "#ffffff",
                  },
                },
              },
            ]);
          }
        });
        args.removed.forEach((cell) => {
          cell.isEdge() && cell.attr("line/strokeDasharray", 0); //正常线
          cell.removeTools();
        });
      });


现在就看到画布上面的内置节点上面的锚点是处于一直显示的状态。 下面给他处理一下。

//需要先在引入的X6方法里添加一个方法
import { Graph,FunctionExt } from "@antv/x6";

在methods里定义一个遍历方法在鼠标移入移出里调用。。

   showPorts(ports, show) {
      for (let i = 0, len = ports.length; i < len; i = i + 1) {
        ports[i].style.visibility = show ? "visible" : "hidden";
      }
    },


然后在方法里给this.garph.on()添加两个鼠标移入移出事件。 移入让锚点显示,移出让锚点隐藏。 这样就可以让画布的节点好看一点了。

//鼠标移入
this.graph.on(
        "node:mouseenter",
        FunctionExt.debounce(() => {
          const container = document.getElementById("containerchart");
          const ports = container.querySelectorAll(".x6-port-body");
          this.showPorts(ports, true);
        }),
        500
      );

//鼠标移出
this.graph.on("node:mouseleave", () => {
        const container = document.getElementById("containerchart");
        const ports = container.querySelectorAll(".x6-port-body");
        this.showPorts(ports, false);
     });

下面是全部的代码

<template>
  <div class="container_warp">
    <div id="containerChart"></div>

    <div
      v-for="(item, index) in List"
      :key="index"
      class="btn"
      :title="item"
      @mousedown="startDrag(item, $event)"
    >
      <span>
        {{ item }}
      </span>
    </div>
  </div>
</template>
<script>
import "@antv/x6-vue-shape";
import { Graph, Shape, FunctionExt } from "@antv/x6";
import { startDragToGraph } from "../methods.js";
export default {
  data() {
    return {
      List: [
        "目录监听",
        "数据组织",
        "影像发布",
        "目标检测",
        "变化检测",
        "地物分类",
        "专家候审",
        "样本入库",
        "影像入库",
        "目标入库",
        "图幅整饰",
        "打包下载",
      ],

      graph: "",
      type: "grid",
      selectCell: "",

      connectEdgeType: {
        //连线方式
        connector: "normal",
        router: {
          name: "",
        },
      },

      grid: {
        // 网格设置
        size: 20, // 网格大小 10px
        visible: true, // 渲染网格背景
        type: "mesh",
        args: {
          color: "#D0D0D0",
          thickness: 1, // 网格线宽度/网格点大小
          factor: 10,
        },
      },
    };
  },

  methods: {
    initX6() {
      this.graph = new Graph({
        container: document.getElementById("containerChart"),
        width: 1000,
        height: 500,
        grid: this.grid,
        connecting: {
          // 节点连接
          anchor: "center",
          connectionPoint: "anchor",
          allowBlank: false,
          snap: true,
        },
        createEdge() {
          return new Shape.Edge({
            attrs: {
              line: {
                stroke: "#1890ff",
                strokeWidth: 1,
                targetMarker: {
                  name: "classic",
                  size: 8,
                },
                strokeDasharray: 0, //虚线
                style: {
                  animation: "ant-line 30s infinite linear",
                },
              },
            },
            label: {
              text: "",
            },
            connector: _that.connectEdgeType.connector,
            router: {
              name: _that.connectEdgeType.router.name || "",
            },
            zIndex: 0,
          });
        },
      });
      // 鼠标移入移出节点
      this.graph.on(
        "node:mouseenter",
        FunctionExt.debounce(() => {
          const container = document.getElementById("containerChart");
          const ports = container.querySelectorAll(".x6-port-body");
          this.showPorts(ports, true);
        }),
        500
      );
      this.graph.on("node:mouseleave", () => {
        const container = document.getElementById("containerChart");
        const ports = container.querySelectorAll(".x6-port-body");
        this.showPorts(ports, false);
      });

      this.graph.on("selection:changed", (args) => {
        args.added.forEach((cell) => {
          this.selectCell = cell;
          if (cell.isEdge()) {
            cell.isEdge() && cell.attr("line/strokeDasharray", 5); //虚线蚂蚁线
            cell.addTools([
              {
                name: "vertices",
                args: {
                  padding: 4,
                  attrs: {
                    strokeWidth: 0.1,
                    stroke: "#2d8cf0",
                    fill: "#ffffff",
                  },
                },
              },
            ]);
          }
        });
        args.removed.forEach((cell) => {
          cell.isEdge() && cell.attr("line/strokeDasharray", 0); //正常线
          cell.removeTools();
        });
      });
    },

    showPorts(ports, show) {
      for (let i = 0, len = ports.length; i < len; i = i + 1) {
        ports[i].style.visibility = show ? "visible" : "hidden";
      }
    },
    // 拖拽生成正方形或者圆形
    startDrag(type, e) {
      console.log(type, e);
      console.log(this.graph);
      startDragToGraph(this.graph, type, e);
    },
  },
  mounted() {
    this.initX6();
  },
};
</script>
<style lang="less">
#containerChart {
  width: 1000px;
  height: 500px;
  border: 1px solid red;
  float: left;
}
.btn {
  width: 400px;
  float: right;
  margin-right: 500px;
}
</style>

  这样就完成了一个手动生成节点到画布的X6了  剩下的就是一些CSS的问题了。 下面写几个X6的事件。方便以后用到或者查阅

this.garph.on('node:click',()=>{

    //这是点击节点事件

})

  this.graph.on("blank:click", () => {

       //这是点击画布背景事件

});

this.graph.on("cell:click", () => {

        //这是点击连接线事件

});

 // 删除节点
deleteNode() {
   const cell = this.graph.getSelectedCells();
   this.graph.removeCells(cell);
   this.type = "grid";
 },
 //清空画布
deleteAll() {
   this.graph.clearCells();
 },
 //销毁画布
GraphDelete() {
   this.graph.dispose();
},


// 保存png
saveToPNG() {
  //下面保存图片
 this.$nextTick(() => {
   this.graph.toPNG(
     (dataUri) => {
       console.log(dataUri);
       // 下载
       DataUri.downloadDataUri(dataUri, "资产拓扑图.png");
     },
     {
       backgroundColor: "white",
       padding: {
         top: 50,
         right: 50,
         bottom: 50,
         left: 50,
       },
       quality: 1,
       copyStyles: false,
     }
   );

   });
 },



this.graph.toJSON()获取当前画布里的节点所有内容
JSON.stringify(this.graph.toJSON())可以把当前画布的所有节点内容转成JSON串存到本地或者后台
JSON.parse(json); 把json数据整形成数据格式 然后通过fromJSON方法再渲染到画布上。
this.graph.fromJSON(json); 可以把从后台或者本地获取到的json数据整形后渲染到画布上。

下面是个人写的一些经验, 由于网上资料有限,记录一下供以后方便查阅, 写的不好各位大神别喷。  有什么问题的也可以留言私信我。

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐