这是我实际开发项目中,利用 X6 开发的一个关系图。具备连线功能。这里我尽可能全的记录整个开发思路和部分编码,如果你也用了 X6 希望对你有帮助。

创建画布

代码有删减,以下展示的代码全都有删减
index.vue

<template><div id="dag-view"></div>
</template><script>
import { Graph } from '@antv/x6'
import { GraphCells } from './GraphCells'
export default {name: 'index',data () {return {graph: null,graphCells: new GraphCells()}},methods: {init () {this.graphCells.clear()this.graph = new Graph(this.graphCells.graphOptions({}))this.graphCells.graph = this.graph}},mounted () {this.init()}
}
</script>

GraphCells.js

class GraphCells {constructor () {this.graph = nullthis.cells = new Map()this.edges = []}clear () {this.graph = nullthis.cells.clear()this.edges = []}graphOptions (options) {return {container: document.getElementById('dag-view'),autoResize: true,// 是否可以拖动panning: false,grid: {size: 10,type: 'dot', // 'dot' | 'fixedDot' | 'mesh'visible: true,args: {color: '#a0a0a0', // 网格线/点颜色thickness: 1 // 网格线宽度/网格点大小}},highlighting: {magnetAvailable: {name: 'stroke',args: {attrs: {stroke: '#47C769'}}},magnetAdsorbed: {name: 'stroke',args: {attrs: {fill: '#fff',stroke: '#31d0c6'}}}},mousewheel: {enabled: true,modifiers: 'ctrl',factor: 1.1,maxScale: 1.5,minScale: 0.5},scroller: {enabled: true,pageVisible: false,pageBreak: false,pannable: true},connecting: {// 当 snap 设置为 true 时连线的过程中距离节点或者连接桩 50px 时会触发自动吸附snap: true,// 是否允许连接到画布空白位置的点allowBlank: false,// 是否允许创建循环连线allowLoop: false,// 拖动边时,是否高亮显示所有可用的连接桩或节点highlight: true},...options}}...
}export {GraphCells
}

创建节点

DagNode.js

import { Node, ObjectExt, Dom } from '@antv/x6'class DagNode extends Node {constructor (options) {super(options)this.options = options}
}DagNode.config({zIndex: 2,width: 100,height: 28,markup: [{tagName: 'rect',selector: 'body'},{// 使用 foreignObject 渲染 HTML 片段tagName: 'foreignObject',attrs: {},children: [{// 当 tagName 指定的标签是 HTML 元素时,需要使用 HTML 元素的命名空间ns: Dom.ns.xhtml,tagName: 'body',attrs: {xmlns: Dom.ns.xhtml},style: {display: 'table-cell',// 设置上左和下左边框radius。在svg元素中很难做到一侧radius,所以这里选择html元素borderTopLeftRadius: '4px',borderBottomLeftRadius: '4px',// 背景颜色(下面介绍怎么动态设置)backgroundColor: '',textAlign: 'center',verticalAlign: 'middle',height: 28,padding: '0 5px'},children: [{tagName: 'i',attrs: {// 设置图标字体classclass: 'iconfont iconzirenwu'},style: {fontSize: '16px',// 图标颜色(下面介绍怎么动态设置)color: ''}}]}]},{tagName: 'text',selector: 'label'}],attrs: {body: {refWidth: '100%',refHeight: '100%',strokeWidth: 0,fill: '#ffffff',// stroke: '#5F95FF',rx: 4,ry: 4,filter: {name: 'highlight',args: {color: '#BFBFBF',width: 2,blur: 2,opacity: 0.5}}},label: {textWrap: {// text: 'lorem ipsum dolor lorem ipsum dolor lorem ipsum dolor',ellipsis: true,breakWord: true,width: -35},textAnchor: 'middle',textVerticalAnchor: 'middle',refX: 60,refY: '50%',refWidth: '80%',fontSize: 12,fill: '#333'}},// 定义连接桩ports: {groups: {right: {position: { name: 'right' },zIndex: 2,attrs: {portBody: {magnet: true,r: 3}}},in: {position: { name: 'left' },zIndex: 2,attrs: {portBody: {magnet: false,r: 0}}},out: {position: { name: 'right' },zIndex: 2,attrs: {portBody: {magnet: false,r: 0}}}}},portMarkup: {tagName: 'circle',selector: 'portBody',attrs: {fill: '#fff',stroke: '#F08BB4',strokeWidth: 1}},propHooks (metadata) {const { label, ...others } = metadataif (label) {ObjectExt.setByPath(others, 'attrs/label/textWrap/text', label)}return others}
})Node.registry.register('dag-node', DagNode, true)export default DagNode

在 index.vue 中引,并添加两个节点到画布中

<template><div id="dag-view"></div>
</template><script>
import { Graph } from '@antv/x6'
import { GraphCells } from './GraphCells'
// 注册自定义节点
import './DagNode'
export default {name: 'index',data () {return {graph: null,graphCells: new GraphCells()}},methods: {init () {this.graphCells.clear()this.graph = new Graph(this.graphCells.graphOptions({}))this.graphCells.graph = this.graph// 添加节点this.graph.addNode({shape: 'dag-node',x: 100,y: 100,label: 'foofoo' // 显示文本。这样写归功于自定义节点中的propHooks})this.graph.addNode({shape: 'dag-node',x: 300,y: 100,label: 'barbar'})}},mounted () {this.init()}
}
</script>

显示效果:

创建连线

DagEdge.js

import { Shape, Edge } from '@antv/x6'/* 连线1 */
class CommonEdge extends Shape.Edge {// ...
}
CommonEdge.config({zIndex: 1,router: {name: 'er',args: {offset: 24,direction: 'H'}},connector: 'rounded',connectionPoint: 'boundary',attrs: {line: {stroke: '#BFBFBF',strokeWidth: 1,targetMarker: null}}
})
Edge.registry.register('common-edge', CommonEdge, true)export {CommonEdge
}

在 index.vue 中引入 DagEdge,并添加连线

<template><div id="dag-view"></div>
</template><script>
import { Graph } from '@antv/x6'
import { GraphCells } from './GraphCells'
// 注册自定义节点
import './DagNode'
// 引入自定义连线
import './DagEdge'
export default {name: 'index',data () {return {graph: null,graphCells: new GraphCells()}},methods: {init () {this.graphCells.clear()this.graph = new Graph(this.graphCells.graphOptions({}))this.graphCells.graph = this.graph// 添加节点this.graph.addNode({id: 'node1',shape: 'dag-node',x: 100,y: 100,label: 'foofoo',// 创建连接桩ports: {items: [{id: 'node1_out',group: 'out'}]}})this.graph.addNode({id: 'node2',shape: 'dag-node',x: 300,y: 100,label: 'barbar',ports: {items: [{id: 'node2_in',group: 'in'}]}})this.graph.addEdge({shape: 'common-edge',source: { cell: 'node1', port: 'node1_out' },target: { cell: 'node2', port: 'node2_in' }})}},mounted () {this.init()}
}
</script>

此时显示效果:

自定义连接

如果再添加一个节点 bazbaz,此时我想手动在 barbar 和 bazbaz 之间连接一条线,这条线和已有的线还不一样,该怎么做呢?
在 DagEdge 中添加另一条自定义连线:

import { Shape, Edge, Graph } from '@antv/x6'/* 连线1 */
class CommonEdge extends Shape.Edge {// ...
}
CommonEdge.config({zIndex: 1,router: {name: 'er',args: {offset: 24,direction: 'H'}},connector: 'rounded',connectionPoint: 'boundary',attrs: {line: {stroke: '#BFBFBF',strokeWidth: 1,targetMarker: null}}
})
Edge.registry.register('common-edge', CommonEdge, true)/* 连线2 */
class RelationEdge extends Shape.Edge {// hover加粗线hoverLine () {this.attr('line', {strokeWidth: 8})}// 清除hoverclearHover () {this.attr('line', {strokeWidth: 1})}// 清除箭头和虚线clearMarker () {this.attr('line', {strokeDasharray: '',targetMarker: ''})}
}
RelationEdge.config({zIndex: 1,connector: 'rounded',connectionPoint: 'boundary',router: {name: 'oneSide',args: { side: 'right' }},attrs: {line: {stroke: '#F08BB4',strokeWidth: 1,strokeDasharray: 5, // 控制虚线间隔targetMarker: {name: 'classic',size: 5}}}
})
Edge.registry.register('relation-edge', RelationEdge, true)export {CommonEdge,RelationEdge
}

在 GraphCells.js 中添加

import { RelationEdge } from './DagEdge'
graphOptions (options) {return {...connecting: {...// 连接的过程中创建新的边createEdge () {return new RelationEdge()},// 在移动边的时候判断连接是否有效,如果返回 false,当鼠标放开的时候,不会连接到当前元素,否则会连接到当前元素validateConnection ({ sourceView, targetView, targetMagnet }) {if (!targetMagnet) {return false}if (targetMagnet.getAttribute('port-group') !== 'right') {return false}return true}}}}

在 index.vue 中添加 bazbaz 节点

<template><div id="dag-view"></div>
</template><script>
import { Graph } from '@antv/x6'
import { GraphCells } from './GraphCells'
// 注册自定义节点
import './DagNode'
// 引入自定义连线
import './DagEdge'
export default {name: 'index',data () {return {graph: null,graphCells: new GraphCells()}},methods: {init () {this.graphCells.clear()this.graph = new Graph(this.graphCells.graphOptions({}))this.graphCells.graph = this.graph// 添加节点this.graph.addNode({id: 'node1',shape: 'dag-node',x: 100,y: 100,label: 'foofoo',ports: {items: [{id: 'node1_out',group: 'out'}]}})this.graph.addNode({id: 'node2',shape: 'dag-node',x: 300,y: 100,label: 'barbar',ports: {items: [{id: 'node2_in',group: 'in'},{id: 'node3_right',group: 'right'}]}})this.graph.addNode({id: 'node3',shape: 'dag-node',x: 300,y: 200,label: 'bazbaz',ports: {items: [{id: 'node3_right',group: 'right'}]}})this.graph.addEdge({shape: 'common-edge',source: { cell: 'node1', port: 'node1_out' },target: { cell: 'node2', port: 'node2_in' }})}},mounted () {this.init()}
}
</script>

显示效果:

添加事件

当鼠标移到连线上时,线加粗,并且显示删除按钮。
index.vue

<template><div id="dag-view"></div>
</template><script>
import { Graph } from '@antv/x6'
import { GraphCells } from './GraphCells'
// 注册自定义节点
import './DagNode'
// 引入自定义连线
import {RelationEdge} from './DagEdge'
export default {name: 'index',data () {return {graph: null,graphCells: new GraphCells()}},methods: {init () {this.graphCells.clear()this.graph = new Graph(this.graphCells.graphOptions({}))this.graphCells.graph = this.graph// 添加节点this.graph.addNode({id: 'node1',shape: 'dag-node',x: 100,y: 100,label: 'foofoo',ports: {items: [{id: 'node1_out',group: 'out'}]}})this.graph.addNode({id: 'node2',shape: 'dag-node',x: 300,y: 100,label: 'barbar',ports: {items: [{id: 'node2_in',group: 'in'},{id: 'node3_right',group: 'right'}]}})this.graph.addNode({id: 'node3',shape: 'dag-node',x: 300,y: 200,label: 'bazbaz',ports: {items: [{id: 'node3_right',group: 'right'}]}})this.graph.addEdge({shape: 'common-edge',source: { cell: 'node1', port: 'node1_out' },target: { cell: 'node2', port: 'node2_in' }})/* 添加事件 */this.graph.on('edge:mouseenter', ({ edge }) => {if (edge instanceof RelationEdge) {// 在 RelationEdge 中已定义edge.hoverLine()// X6 提供的小工具 https://x6.antv.vision/zh/docs/api/registry/edge-tooledge.addTools([{name: 'button-remove',args: {distance: '50%',offset: 0,// 删除回调// onClick ({ cell }) {// }}}])}})this.graph.on('edge:mouseleave', ({ edge }) => {if (edge instanceof RelationEdge) {edge.clearHover()edge.removeTools()}})this.graph.on('edge:connected', ({ isNew, edge }) => {if (isNew) {edge.clearMarker()}})}},mounted () {this.init()}
}
</script>

此时效果:

使用布局

使用布局使节点按照一定形式排列
index.vue

<template><div id="dag-view"></div>
</template><script>
import { Graph } from '@antv/x6'
import { GraphCells } from './GraphCells'
import { DagreLayout } from '@antv/layout'
// 注册自定义节点
import './DagNode'
// 引入自定义连线
import {RelationEdge} from './DagEdge'
export default {name: 'index',data () {return {graph: null,dagreLayout: null,graphCells: new GraphCells()}},methods: {init () {this.graphCells.clear()this.graph = new Graph(this.graphCells.graphOptions({}))this.graphCells.graph = this.graph/* 添加事件 */this.graph.on('edge:mouseenter', ({ edge }) => {if (edge instanceof RelationEdge) {// 在 RelationEdge 中已定义edge.hoverLine()// X6 提供的小工具 https://x6.antv.vision/zh/docs/api/registry/edge-tooledge.addTools([{name: 'button-remove',args: {distance: '50%',offset: 0,// 删除回调// onClick ({ cell }) {// }}}])}})this.graph.on('edge:mouseleave', ({ edge }) => {if (edge instanceof RelationEdge) {edge.clearHover()edge.removeTools()}})this.graph.on('edge:connected', ({ isNew, edge }) => {if (isNew) {edge.clearMarker()}})this.dagreLayout = new DagreLayout({type: 'dagre',rankdir: 'LR',align: undefined,ranksep: 50,nodesep: 5,controlPoints: true,begin: [50, 80]})},// 布局渲染graphLayout () {const newModel = this.dagreLayout.layout(this.graphCells.getModel())this.graph.fromJSON(newModel)this.graph.centerContent()},getData () {// 接口获取节点和连线信息。添加到 graphCells 中// this.graphCells.setCell(cell)//  this.graphCells.setEdge(edge)this.$nextTick(() => {this.graph && this.graphLayout()})}},created () {this.getData()},  mounted () {this.init()}
}
</script>

GraphCell.js

import { Dom, Shape } from '@antv/x6'
import { RelationEdge } from './DagEdge'class GraphCells {constructor () {this.graph = nullthis.cells = new Map()this.edges = []}clear () {this.graph = nullthis.cells.clear()this.edges = []}graphOptions (options) {return {container: document.getElementById('dag-view'),autoResize: true,// 是否可以拖动panning: false,grid: {size: 10,type: 'dot', // 'dot' | 'fixedDot' | 'mesh'visible: true,args: {color: '#a0a0a0', // 网格线/点颜色thickness: 1 // 网格线宽度/网格点大小}},highlighting: {magnetAvailable: {name: 'stroke',args: {attrs: {stroke: '#47C769'}}},magnetAdsorbed: {name: 'stroke',args: {attrs: {fill: '#fff',stroke: '#31d0c6'}}}},mousewheel: {enabled: true,modifiers: 'ctrl',factor: 1.1,maxScale: 1.5,minScale: 0.5},scroller: {enabled: true,pageVisible: false,pageBreak: false,pannable: true},connecting: {// 当 snap 设置为 true 时连线的过程中距离节点或者连接桩 50px 时会触发自动吸附snap: true,// 是否允许连接到画布空白位置的点allowBlank: false,// 是否允许创建循环连线allowLoop: false,// 拖动边时,是否高亮显示所有可用的连接桩或节点highlight: true,createEdge () {return new RelationEdge()},validateConnection ({ sourceView, targetView, targetMagnet }) {if (!targetMagnet) {return false}if (targetMagnet.getAttribute('port-group') !== 'right') {return false}return true}},...options}}// 根据节点类型生成连接桩getPortItems (id) {const items = []const inPort = {id: `port_${id}_in`,group: 'in'}const outPort = {id: `port_${id}_out`,group: 'out'}const rightPort = {id: `port_${id}_right`,group: 'right'}return items}/*** @function 添加节点* */setCell (options) {const id = options.idconst cell = {shape: 'dag-node',// 注意添加节点数据时传入的makup会覆盖DagNode.config中的markup。// 所以DagNode中的markup可以删除,在这动态定义,就可以根据节点类型不同改变色值和图标markup: [{tagName: 'rect',selector: 'body'},{tagName: 'foreignObject',attrs: {},children: [{ns: Dom.ns.xhtml,tagName: 'body',attrs: {xmlns: Dom.ns.xhtml},style: {display: 'table-cell',borderTopLeftRadius: '4px',borderBottomLeftRadius: '4px',// 动态传入颜色backgroundColor: '',textAlign: 'center',verticalAlign: 'middle',height: 28,padding: '0 5px'},children: [{tagName: 'i',attrs: {// 动态传入图标classclass: ''},style: {fontSize: '16px',// 动态传入颜色color: ''}}]}]},{tagName: 'text',selector: 'label'}],ports: {items: this.getPortItems(id)},...options}this.cells.set(id, cell)}/*** @function 添加连线* @param edge {object} 连线配置* */setEdge (edge) {const { source, target } = edgeif (this.cells.has(source.cell) && this.cells.has(target.cell)) {this.edges.push({// 默认普通连线shape: 'common-edge',...edge})}}/* graph model */getModel () {return {nodes: [...this.cells.values()],edges: this.edges}}
}export {GraphCells
}

Tooltip

当节点 label 文本过长会显示 … ,鼠标移入显示 tooltip 显示全名。在 X6 中没有找到合适的小工具,我自己写了一个 Tooltip 组件。这里只是配合我的代码结构使用,并非封装完美的 Tooltip 组件。仅供大家参考。
Tooltip.vue

<template><div id="lz_tooltip_container" class="lz_tooltip_container"><div class="tooltip"><span class="arrow"></span><div class="cont"></div></div></div>
</template><script>
export default {name: 'Tooltip'
}
</script><style lang="scss">.lz_tooltip_container {position: fixed;left: -1000px;top: -1000px;z-index: 999;}.lz_tooltip_container > .tooltip {position: absolute;background: black;color: white;top: -40px;font-size: 14px;padding: 8px 16px;border-radius: 5px;}.lz_tooltip_container > .tooltip > .cont {white-space: nowrap}.lz_tooltip_container > .tooltip > .arrow {position: absolute;width: 0;height: 0;bottom: -6px;left: 24px;border-left: 6px solid transparent;border-right: 6px solid transparent;border-top: 6px solid #000000;filter: drop-shadow(0 2px 12px rgba(0,0,0, .03));}.lz_tooltip_container > .tooltip_hidden {display: none;}
</style>

我使用的是相对于屏幕的固定定位 fixed。因为在画布会上下滚动,左右平移,所以要根据节点相对画布的位置,转换成相对屏幕的位置然后动态设置 Tooltip 的 left top 属性。
在 index.vue 中 添加事件

<template><div id="dag-view"></div>
</template><script>
import Tooltip from './Tooltip'
export default {name: 'index',components: {Tooltip},methods: {init () {...this.graph.on('node:mouseenter', ({ e, node, view }) => {// 节点相对画布位置const pos = node.position({ relative: true })// 转为相对屏幕位置const { x, y } = this.graph.localToClient(pos.x, pos.y)const tooltip = document.getElementById('lz_tooltip_container')if (!tooltip) returnconst cont = tooltip.querySelector('.cont')if (!cont) returncont.innerText = node.options.labeltooltip.style.left = `${x + 20}px`tooltip.style.top = `${y - 5}px`})this.graph.on('node:mouseleave', ({ node }) => {const tooltip = document.getElementById('lz_tooltip_container')if (!tooltip) returntooltip.style.left = '-1000px'tooltip.style.top = '-1000px'})}}
}
</script>

效果展示:

Antv X6 动态连线相关推荐

  1. VUE中使用Antv.X6实现将侧边导航栏节点拖拽到画布中,并能够将画布保存到缓存中

    直接上代码,参考官网的文档安装完之后可以直接运行. <template><div class="home"><div class="left ...

  2. 【VUE3+AntV X6】 引入ANTV X6 的流程图编辑器应用(一)

    ANTV X6 流程图编辑器应用 唠唠叨叨 事发背景 实现效果 实现思路 总结 参考资料 唠唠叨叨 唉!最近感觉非常忙,要学的东西太多,要实现的方法变幻莫测,层出不穷,越学越觉得自己不会的实在太多.项 ...

  3. 在 Vue3 中使用 AntV X6

    在 Vue3 中使用 AntV X6 图编辑引擎 <script setup lang="ts"> import { onMounted, ref } from 'vu ...

  4. antV x6 开发流程图实例

    1)使用x6背景 之前本来是使用jsplumb社区版开发完成的,但是由于jsplumb社区版缩放功能不能满足,所以经过对比其他的流程图插件果断换x6.x6基本满足目前所有需求.使用起来还算顺手. 2) ...

  5. 记录--VUE使用antv X6实现流程图

    安装npm install @antv/x6 --save 引入import { Graph, Addon } from "@antv/x6"; 说明:如果没有自定义拖拽到画布的需 ...

  6. AntV X6 流程图

    安装 AntV X6  npm install @antv/x6 --save html结构 <div class="editor" ref="editor&quo ...

  7. Vue3 项目中使用 AntV X6 绘制流程图

    X6 是 AntV 旗下的图编辑引擎,提供了一系列开箱即用的交互组件和简单易用的节点定制能力,方便我们快速搭建流程图.DAG 图.ER 图等图应用. 参考一些网站的写法,然后自己加上了撤销操作. 节点 ...

  8. Vue项目中使用AntV X6绘制流程图

    Vue项目中使用AntV X6绘制流程图 一.需求 在Vue2.x(Vue3.x项目同理)项目中使用AntV X6组件库绘制流程图,需要实现以下需求: 需求1:左侧菜单中的模块可以拖拽进入画布中生成对 ...

  9. 使用antv/X6实现流程图(包括线条动画流动,路径标签,悬浮窗口等)快速搭建流程图,DAG图等初始实践记录

    文章目录 开始使用 Step 1 创建容器 Step 2 准备数据 Step 3 渲染画布 取消节点的移动 使用不同图形渲染节点 画布居中和缩放 修改边样式 给虚线增加流动效果 修改节点和边的属性 鼠 ...

  10. OpenGL进阶演示样例1——动态画线(虚线、实线、颜色、速度等)

    用OpenGL动态绘制线段.事实上非常easy,但到如今为止.网上可參考资料并不多. 于是亲自己主动手写一个函数,方便动态绘制线段.代码例如以下: #include<GL/glut.h> ...

最新文章

  1. 两小时入门 Docker
  2. vault-使用kubernetes作为认证后端
  3. jsp/servlet与ajax实现简单局部日期时间更新
  4. [python] 溜了,溜了,七牛云图片资源批量下载 自建图床服务器
  5. python代码案例详解-Python综合应用名片管理系统案例详解
  6. VMware下安装Ubuntu不支持1920X1080分辨率问题
  7. CSS+jQuery/JavaScript图片切换播放
  8. java并发问题_并发理论基础:并发问题产生的三大根源
  9. hadoop伪分布式配置
  10. 高速通道-冗余物理专线接入-健康检查配置
  11. 【SPOJ - DQUERY】D-query【主席树 —— 区间中不同数的个数】
  12. Arduino的软件:Arduino IDE和Mind+图形化编程
  13. 科学研究设计一:什么是科学
  14. cloudflare-5s盾分析
  15. 一名高级软件测试工程师,需要具备哪些能力
  16. C# Form窗体显示不完整
  17. SegFormer论文记录(详细翻译)
  18. 第十七届全国大学智能车竞赛赛场合影集锦
  19. 【我的新书】分布式微服务架构:原理与实战
  20. html内嵌框架怎么写,在HTML网页中,内嵌框架的标签代码是()。

热门文章

  1. 狂神说Linux03:Vim使用及账号用户管理
  2. “贵阳发生了什么”大数据主题报告会在京举行
  3. 想转行程序员,参加java培训班真的有用吗?
  4. python半圆,(小小黑科技)vue+echarts实现半圆图表
  5. 去除Multiple markers at this line - advised by提示
  6. 刘夏真的简历中国科学院计算机所,一个中科院,四个985,还有一个志愿留在本校,这个学霸考研宿舍是怎样炼成的?...
  7. InstallShield
  8. 学习andriod开发之 异步加载图片(二)--- 使用其他进度条
  9. 高等数学期末总复习 DAY6.洛必达求极限、不等式单调性证明、判断拐点、曲率以及曲率半径
  10. 程序员月薪过7万,可以落户北京了