
react使用AntvX6实现流程图
如何使用AntV X6在react中插入流程图功能?
·
如何使用AntV X6在react中插入流程图功能?
一、创建画布
首先要先创建一个id为container的div盒子,然后
this.graph = new Graph({
container: document.getElementById('container')!,//获取盒子
width: 800,
height: 600,
// 网格
grid: {
size: 10,
visible: true,
type: 'dot',//默认是点状,也可以选择mesh
args: [
{
color: '#a0a0a0',//网格的颜色
thickness: 1.5,//网格宽度
},
],
},
panning: {
enabled: true,
eventTypes: ['leftMouseDown', 'rightMouseDown', 'mouseWheel'],
modifiers: 'ctrl',
},
// 鼠标滚轮的默认行为是滚动页面
mousewheel: {
enabled: true,
zoomAtMousePosition: true,
modifiers: 'ctrl',
minScale: 0.5,
maxScale: 3,
},
// 节点连接
connecting: {
router: 'orth', //manhattan
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
anchor: 'center',
connectionPoint: 'anchor',
snap: true, // 自动吸附
allowBlank: false, // 是否允许连接到画布空白位置的点
allowLoop: false, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点
allowNode: false, // 是否允许边链接到节点(非节点上的链接桩)
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: '#A2B1C3',
strokeWidth: 1,
targetMarker: {
name: 'block',
width: 12,
height: 8,
},}
},
zIndex: 0,
})
},
validateConnection({ targetMagnet }) {
return !!targetMagnet
},
},
// 高亮
highlighting: {
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#D06269',
stroke: '#D06269',
},
},
},
},
resizing: true, // 缩放节点,默认禁用
rotating: true, // 旋转节点,默认禁用
// 启动选择节点
selecting: {
enabled: true,
rubberband: true,
showNodeSelectionBox: true,
},
snapline: true, // 对齐线
keyboard: true, // 键盘快捷键,默认禁用
history: true, // 启动历史记录
// 小地图,默认禁用
minimap: {
enabled: true,
container: document.getElementById('minimap')!,
width: 198,
height: 198,
padding: 10,
},
clipboard: true, // 剪切板,默认禁用
})
在画布中可以定义画布的大小、网格类型(默认是点状,可以选择珊格,还可以自定义)、背景颜色等等
二、创建左侧流程图画板
private static initStencil() {
const self = this
this.stencil = new Addon.Stencil({
title: 'FlowGraph',
target: this.graph,
stencilGraphWidth: 214,
stencilGraphHeight: document.body.offsetHeight - 96,//-96
layoutOptions: {
columns: 2,
columnWidth: 80,
rowHeight: 60,
marginY: 30,
marginX: 10
},
//获取拖拉节点
getDropNode(node: any) {
const size = node.size()
const { type } = node.store.data
const label: String = self.getLabel(type)
return node.clone().size(size.width * 1, size.height * 1).attr('label/text', label) //右侧获取label,这里是从画板上拖拉下来的节点的大小,这里*1表示,大小不变,与画板上的节点大小一致
// return node.clone().size(size.width * 1, size.height * 1)
}
})
const stencilContainer = document.querySelector('#stencil')!
if (stencilContainer) {
stencilContainer.appendChild(this.stencil.container)
}
}
其中getLabel:
private static getLabel(type: String) {
let label: String = ''
switch(type) {
case 'rect':
label = '矩形节点'
break
case 'rect-radius':
label = '圆角矩形节点'
break
case 'polygon-rhombus':
label = '菱形节点'
break
case 'polygon-rhomboid':
label = '四边形节点'
break
case 'circle':
label = '圆形节点'
break
}
return label
}
三、给左侧画板添加流程图节点样式
private static initShape() {
const graph = this.graph
// #region 初始化图形
//定义连接桩
const ports = {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden', //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',
},
],
}
//定义节点
Graph.registerNode(
'custom-rect',
{
inherit: 'rect',
width: 66,
height: 36,
attrs: {
body: {
strokeWidth: 1,
stroke: '#5F95FF',
fill: '#EFF4FF',
},
text: {
fontSize: 12,
fill: '#262626',
},
},
ports: { ...ports },
},
true,
)
Graph.registerNode(
'custom-polygon',
{
inherit: 'polygon',
width: 66,
height: 36,
attrs: {
body: {
strokeWidth: 1,
stroke: '#5F95FF',
fill: '#EFF4FF',
},
text: {
fontSize: 12,
fill: '#262626',
},
},
ports: {
...ports,
items: [
{
group: 'top',
},
{
group: 'bottom',
},
],
},
},
true,
)
Graph.registerNode(
'custom-circle',
{
inherit: 'circle',
width: 45,
height: 45,
attrs: {
body: {
strokeWidth: 1,
stroke: '#5F95FF',
fill: '#EFF4FF',
},
text: {
fontSize: 12,
fill: '#262626',
},
},
ports: { ...ports },
},
true,
)
Graph.registerNode(
'custom-image',
{
inherit: 'rect',
width: 52,
height: 52,
markup: [
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'image',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
body: {
stroke: '#5F95FF',
fill: '#5F95FF',
},
image: {
width: 26,
height: 26,
refX: 13,
refY: 16,
},
label: {
refX: 3,
refY: 2,
textAnchor: 'left',
textVerticalAnchor: 'top',
fontSize: 12,
fill: '#fff',
},
},
ports: { ...ports },
},
true,
)
//定义边的样式
// Graph.registerEdge(
// 'custom-edge',{
// inherit: 'edge',
// attrs: {
// line: {
// stroke: '#A2B1C3',
// strokeWidth: 1,
// targetMarker: {
// name: 'block',
// width: 12,
// height: 8,
// },
// },
// },
// ports: { ...ports }
// }
// )
const r1 = graph.createNode({
shape: 'custom-rect',
label: '开始',
attrs: {
body: {
rx: 20,
ry: 26,
},
},
ports: { ...ports }
})
const r2 = graph.createNode({
shape: 'custom-rect',
label: '过程',
ports: { ...ports }
})
const r3 = graph.createNode({
shape: 'custom-rect',
attrs: {
body: {
rx: 6,
ry: 6,
},
},
label: '可选过程',
ports: { ...ports }
})
const r4 = graph.createNode({
shape: 'custom-polygon',
attrs: {
body: {
refPoints: '0,10 10,0 20,10 10,20',
},
},
label: '决策',
ports: { ...ports }
})
const r5 = graph.createNode({
shape: 'custom-polygon',
attrs: {
body: {
refPoints: '10,0 40,0 30,20 0,20',
},
},
label: '数据',
ports: { ...ports }
})
const r6 = graph.createNode({
shape: 'custom-circle',
label: '连接',
ports: { ...ports }
})
this.stencil.load([r1, r2, r3, r4, r5, r6])
}
在这里可以注册节点和边的样式,节点和边以及连接桩的样式均可以自定义。连接桩就是节点的连接点,对流程图而言一般默认是上下左右四个。
四、初始化注册事件
private static initEvent() {
const graph = this.graph
const container = document.getElementById('container')!
// 节点鼠标移入
graph.on('node:mouseenter', FunctionExt.debounce((nodeAttr: any) => {
// 显示连接点
const ports = container.querySelectorAll(
'.x6-port-body',
) as NodeListOf<SVGElement>
this.showPorts(ports, true)
// 添加删除
const { node } = nodeAttr
const { width } = node.store.data.size
node.addTools({
name: 'button-remove',
args: {
x: 0,
y: 0,
offset: { x: 0, y: 0 },
},
})
}), 500)
// 节点鼠标移出
graph.on('node:mouseleave', ({ node }) => {
const ports = container.querySelectorAll(
'.x6-port-body',
) as NodeListOf<SVGElement>
this.showPorts(ports, false)
// 移除删除
node.removeTools()
})
// 连接线鼠标移入
graph.on('edge:mouseenter', ({ edge }) => {
// 添加删除
edge.addTools([
'source-arrowhead',
'target-arrowhead',
{
name: 'button-remove',
args: {
distance: -30,
}
}
])
})
graph.on('edge:mouseleave', ({ edge }) => {
// 移除删除
edge.removeTools()
})
}
设置节点和边的鼠标事件。
至此基础功能就可以实现了。
后面还可以添加toolbar实现保存、撤销、重做、删除、放大缩小等功能。官网有具体的实现方法。
AntV X6有提供graph.fromJSON()和toJSON()的方法可以将图转换为数据,再将数据转换为图。可以使用这个实现在localStorage中的保存。
另外,节点可以添加修改功能编辑节点的名称。
参考:react-antvx6-app: react typescript 运用 @antv-x6、antd、@antv/x6-react-components 实现基本流程图编辑
更多推荐
所有评论(0)