antv x6 绘制流程图
因业务需要,要把节点之间的连线方式由连接桩改为键盘按键加鼠标的形式,并添加连线模式按钮。
下面来看下实现步骤,以shift键为例
- 画布配置文件
import {Graph, Edge} from '@antv/x6'
import {message} from 'antd'
export default class FlowGraph {
public static graph: Graph
public static shiftPressed: boolean //shift键是否按下
//画布初始化
public static init() {
this.graph = new Graph({
container: document.getElementById('container')!,
width: 1000,
height: 800,
background: {color: '#040B22'},
resizing: {
enabled: false,
},
grid: {
size: 20,
visible: true,
type: 'mesh',
args: [
{
color: 'rgba(255,255,255,0.1)',
thickness: 1,
},
],
},
selecting: {
pointerEvents: 'auto',
enabled: true,
multiple: true,
rubberband: true,
movable: false,
showNodeSelectionBox: true,
following: false,
},
connecting: {
allowEdge: false,
allowMulti: false,
allowLoop: false,
allowBlank: false,
validateConnection({sourceView, targetView, sourceMagnet, targetMagnet}) {
if (sourceView === targetView) {
return false
}
if (!sourceMagnet) {
return false
}
if (!targetMagnet) {
return false
}
return true
},
},
highlighting: {
//高亮样式
// 拖动节点进行嵌入操作过程中,节点可以被嵌入时被使用。
embedding: {
name: 'stroke',
args: {
padding: -1,
attrs: {
stroke: '#73d13d',
},
},
},
},
snapline: true,
history: true,
clipboard: true,
keyboard: true,
embedding: {
enabled: false,
findParent({node}) {
const bbox = node.getBBox()
return this.getNodes().filter((node) => {
const data = node.getData<any>()
if (data && data.parent) {
const targetBBox = node.getBBox()
return bbox.isIntersectWithRect(targetBBox)
}
return false
})
},
},
scroller: {
enabled: true,
},
mousewheel: {
enabled: true,
modifiers: ['ctrl', 'meta'],
minScale: 0.1,
maxScale: 3,
},
interacting: {
nodeMovable: true,
},
})
this.initEvent()
return this.graph
}
private static initEvent() {
const {graph} = this
}
}
- 首先画布绑定shift键按下抬起事件,按下时节点设置不可移动
private static initEvent() {
const {graph} = this
graph.bindKey(
'shift',
() => {
FlowGraph.setShiftPressed(true)
},
'keydown',
)
graph.bindKey(
'shift',
() => {
FlowGraph.setShiftPressed(false)
},
'keyup',
)
}
public static setShiftPressed(shiftPressed: boolean) {
this.shiftPressed = shiftPressed
if (shiftPressed) {
(FlowGraph.graph.options.interacting as any).nodeMovable = false // 节点不可以移动
} else {
(FlowGraph.graph.options.interacting as any).nodeMovable = true // 节点可以移动
}
}
- 接着监听画布节点鼠标按下事件,先判断此时shift键是否按下,如果按下则创建连线,线的起点为按下的节点,终点为鼠标的位置,同时监听鼠标移动事件
let currentEdge: Edge
// 画布节点鼠标按下事件
graph.on("node:mousedown", ({ node, e }) => {
// 点击节点,如果按下shift时,创建连线
if (FlowGraph.shiftPressed) {
currentEdge = graph.addEdge({
source: node,
attrs: {
line: {
stroke: "#d1d2db",
strokeWidth: 1,
},
text: {
type: "AUTO",
},
},
router: {
name: "manhattan",
},
data: {},
});
document.addEventListener("mousemove", handleMouseMove);
// 创建线后,线的指向为鼠标点击的位置
const { clientX, clientY } = e;
const p = this.graph.clientToLocal({
x: clientX,
y: clientY,
});
currentEdge.setTarget(p);
}
});
- 鼠标移动事件,线的终点跟随鼠标位置同步更新
// 鼠标移动事件
const handleMouseMove = (e: MouseEvent) => {
const { clientX, clientY } = e;
const p = this.graph.clientToLocal({ x: clientX, y: clientY });
currentEdge.setTarget(p);
};
- 最后监听鼠标抬起事件,获取当前鼠标位置的节点,如果没有则取消连线,有则判断shift键是否仍是按下状态,然后根据业务需求进行一些场景判断最终设置线的终点
//鼠标抬起事件
graph.on('node:mouseup', ({node, e}) => {
//获取鼠标位置的节点并连接
const {clientX, clientY} = e
const p = this.graph.clientToLocal({x: clientX, y: clientY})
let nodes = graph.getNodesFromPoint(p.x, p.y)
if (nodes.length) {
if (FlowGraph.shiftPressed) {
document.removeEventListener('mousemove', handleMouseMove)
let targetNode = nodes.sort((a: any, b: any) => {
return b.zIndex - a.zIndex
})[0]
if (currentEdge) {
// 判断组合内的节点是否连接到组合外部
if (graph.getCellById((currentEdge.source as any).cell).getParent() != targetNode.getParent()) {
graph.removeEdge(currentEdge)
message.error('组合内的节点不能连接到外部')
}
// 判断线的终点与起点是不是同一个节点
if (node.id != targetNode.id) {
currentEdge.setTarget(targetNode)
} else {
graph.removeEdge(currentEdge)
}
}
}
}else {
// 鼠标位置处没有节点,取消连线
graph.removeEdge(currentEdge)
}
})
至此功能实现,当然也可以添加一个按钮来开启此模式,即点击按钮时shiftPressed设置为true即可。具体效果看以下视频,如有不足之处,请大家多多指正。
连线模式