Antv/X6介绍
简介
X6 是基于 HTML 和 SVG 的图编辑引擎,提供低成本的定制能力和开箱即用的内置扩展,方便我们快速搭建 DAG 图、ER 图、流程图、血缘图等应用。 图编辑核心能力:节点、连线与画布,可使用React、Vue、Angular组件节点
开始使用
创建画布
容器
<div ref="containerRef" class="x6-container"></div>
csharp
import { ref, watch, onMounted, onUnmounted } from "vue"
import { Graph } from "@antv/x6";
const containerRef = ref(ref)
const graph = new Graph({
container: containerRef.value
})
onUnmounted(() => {
graph.dispose() // 销毁
})
尺寸:初始创建可使用width:1200和height:600属性指定画布大小,默认是容器宽高
js
const ports = {
groups: {
top: {
position: "top",
attrs: {
circle: {
r: 4,
magnet: true,
stroke: "#5F95FF",
strokeWidth: 1,
fill: "#fff",
style: {
visibility: "hidden"
}
}
}
},
right: {
position: "right",
attrs: {
circle: {
r: 4,
magnet: true,
stroke: "#5F95FF",
strokeWidth: 1,
fill: "#fff",
style: {
visibility: "hidden"
}
}
}
},
bottom: {
position: "bottom",
attrs: {
circle: {
r: 4,
magnet: true,
stroke: "#5F95FF",
strokeWidth: 1,
fill: "#fff",
style: {
visibility: "hidden"
}
}
}
},
left: {
position: "left",
attrs: {
circle: {
r: 4,
magnet: true,
stroke: "#5F95FF",
strokeWidth: 1,
fill: "#fff",
style: {
visibility: "hidden"
}
}
}
}
},
items: [
{
group: "top"
},
{
group: "right"
},
{
group: "bottom"
},
{
group: "left"
}
]
}
new Graph({
container: containerRef.value,
ports: {
...ports
}
})
使用ports指定连接桩内置有上下左右点,按照节点尺寸设置,也可使用position: [88, 62]具体坐标添加连接桩,节点内部组件可以溢出节点显示(组件尺寸比节点指定尺寸大),可指定位置保证连接桩在显示区域边上
js
const nodeMouseenter = () => {
const container = containerRef.value
const ports = container.querySelectorAll(".x6-port-body")
showPorts(ports, true)
}
const nodeMouseleave = () => {
const container = containerRef.value
const ports = container.querySelectorAll(".x6-port-body")
showPorts(ports, false)
}
graph.on("node:mouseenter", nodeMouseenter)
graph.on("node:mouseleave", nodeMouseleave)
连接桩默认隐藏,使用画布监听事件控制显示隐藏
js
new Graph({
connecting:{
router: "manhattan",
connector: {
name: "rounded",
args: {
radius: 8
}
},
anchor: "center",
connectionPoint: "anchor",
allowBlank: false,
snap: {
radius: 20
},
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: "#1890FF",
strokeWidth: 2,
targetMarker: {
name: "block",
width: 12,
height: 8
}
}
},
zIndex: 0
})
},
validateConnection({ targetMagnet }) {
return !!targetMagnet
}
}
}
Edge(边/连线)使用connecting属性设置,画布手动连接的线会使用此配置,在创建Edge时需要单独设置,边的路径类型,颜色、箭头都可以设置
js
import { Dnd } from "@antv/x6-plugin-dnd"
dnd = new Dnd({
target: graph,
scaled: false,
dndContainer: nodeContainerRef.value,
validateNode: (cls, node) => {
...
return false
}
})
X6通过Dnd组件内置节点拖拽功能,指定节点拖拽容器和画布,validateNode为拖拽回调,可在此控制节点添加行为或者使用内置的Stencil插件创建拖拽部分
js
import { Scroller } from "@antv/x6-plugin-scroller"
graph.use(
new Scroller({
enabled: true,
pannable: false
})
)
X6内置了滚动功能,使用插件开启,可滚动和鼠标拖动,鼠标操作可能会有冲突按使用场景控制
js
graph = new Graph({
container: containerRef.value,
interacting: () => {
return false
},
...
})
javascript
translating: {
restrict(view) {
if (view) {
const cell = view.cell
if (cell.isNode()) {
const parent = cell.getParent()
if (parent) {
return parent.getBBox()
}
}
}
return null
},
},
X6内置多种方法可以控制画布节点拖动行为,可以使用tanslating设置控制,设置{ restrict: false/true }或者指定函数控制(可指定[x,y]范围控制),实际使用中控制节点完全不允许移动,实践下来translating单节点没有问题,Group添加子节点之后父节点移动控制有问题,姿势不对或者是bug,使用interacting属性可以控制节点完全不移动
js
graph.on("node:mouseenter", ({ node }) => {
node.addTools([
{
name: "button-remove",
args: {
x: node.shape === "node-select-point" ? 176 : 260,
y: 0,
onClick({ cell }) {
graph.removeNode(cell)
}
}
}
])
})
graph.on("node:mouseleave", ({ node }) => {
if (node.hasTool("button-remove")) {
node.removeTool("button-remove")
}
})
X6有内置小组件,可动态添加删除按钮删除节点,可使用onClick指定删除行为或者不指定使用默认删除
Node节点
js
graph.createNode({
shape: 'rect',
data: {
},
...
})
可以使用createNode方法或者addNode方法创建节点使用shape指定类型返回值为节点
js
<template>
<TeleportContainer></TeleportContainer>
</template>
import { register, getTeleport } from "@antv/x6-vue-shape"
const TeleportContainer = getTeleport()
register({
shape: '...',
width: 100,
height: 100,
component: ...,
ports: {
...ports
}
})
graph.createNode({
shape: '...',
data: {
parent: null,
children: []
}
})
使用Vue组件节点需要先使用register注册shape,使用时创建节点指定shape名称即可
js
graph.on("cell:added", ({ cell, index, options }) => {
})
graph.on("cell:removed", ({ cell, index, options }) => {
}
可监听事件回调,处理对用逻辑
X6节点有群组概念,可使用节点addChild方法添加子节点建立父子关系,可使用graph.getNodes方法获取节点(注:使用toJSON方法获取的节点没有cell类等继承方法),使用getNodes获取的都是节点类型,Group通过children获取的子节点包含节点和边
Edge边/连线
js
graph.createEdge({
source: { cell: id, port: id : id },
target: { cell: id, port: id},
router: "normal",
attrs: {
line: {
stroke: "#586C90",
strokeWidth: 2,
targetMarker: {
name: "block",
width: 12,
height: 8
}
}
}
})
graph.addEdge(edge)
可使用createEdge或者addEdge方法创建边/连接线,指定连接点可使用节点和连接桩id建立连接或者直接指定连接坐标[x,y]来连线,手动创建连线需要指定连线配置,不共用画布连接线设置。
流程表单
使用Vue节点创建的组件和普通Vue节点没本质区别,但是其是由X6托管,和项目Vue组件之间没有关联关系,内部数据交互需要使用pinia/vuex或者node节点data来管理
js
export default defineComponent({
name: 'ProgressNode',
inject: ['getNode'],
data() {
return {
percentage: 80,
}
},
mounted() {
const node = (this as any).getNode() as Node
node.on('change:data', ({ current }) => {
const { progress } = current
this.percentage = progress
})
},
})
Vue节点内部可使用inject("getNode")获取节点,通过监听节点方法来获取节点数据
js
import { useStore } from "@/store/..."
const store = useStore()
const getNode = inject("getNode")
const node = getNode()
const id = node.id
节点可使用节点id通过pinia来管理节点内部表单数据,外部组件和X6Vue节点交互都需要通过pinia来进行。数据变动通过watch监听pinia变动来更新。
可以通过toJSON和fromJSON方法来保存流程图和还原流程图,X6托管的Vue节点没有生命周期回调,还原表单数据是需要注意等Vue节点都mounted之后再触发pinia数据更新触发内部watch回填表单数据