共计 3296 个字符,预计需要花费 9 分钟才能阅读完成。
开始
在流程图中咱们须要通过拖拽交互往画布中增加节点,X6 不仅内置了弱小的拖拽性能,还内置了很多罕用的图形,接下来咱们一起实现根底的流程图图形以及图形拖拽性能。
实现
图形定义
首先来看下一个简略的矩形节点的根底配置:
graph.addNode({ | |
shape: 'rect', | |
x: 100, | |
y: 100, | |
width: 80, | |
height: 40, | |
attrs: { | |
body: {stroke: 'red'} | |
} | |
}) |
shape:定义图形的形态,X6 内置了 rect、circle、ellipse、polygon、polyline、image、html 等根底形态
x/y:定义图形的左上角坐标
width/height:定义图形的尺寸
看到 attrs 大家可能比拟奇怪,这是什么货色?其实能够将 attrs 看做 css 款式汇合,其中 body 相似于 css 选择器,body 的值是被选中元素的属性。那 body 又是哪来的呢?这里就要说的另一个重要的配置项 markup,markup 示意的是图形的 DOM 构造,内置的 rect 的默认 markup 为:
[ | |
{ | |
tagName: 'rect', | |
selector: 'body', | |
}, | |
{ | |
tagName: 'text', | |
selector: 'label', | |
}, | |
] |
渲染实现后,理论失效的 DOM 为:
<g data-cell-id="ca715562-8faf-4c88-a242-2b18d4ce47a6" data-shape="rect" class="x6-cell x6-node" transform="translate(100,100)"> | |
<rect fill="#ffffff" stroke="red" stroke-width="2" width="80" height="40"></rect> | |
<text font-size="14" fill="#000000" text-anchor="middle" text-vertical-anchor="middle" font-family="Arial, helvetica, sans-serif" transform="matrix(1,0,0,1,40,20)"></text> | |
</g> |
所以说一个图形是由 markup 和 attrs 来决定构造和款式,咱们能够通过设置 markup 和 attrs 来定义本人业务中的图形,看上面的例子:
graph.addNode({ | |
shape: 'rect', | |
x: 60, | |
y: 60, | |
width: 70, | |
height: 70, | |
markup: [ | |
{ | |
tagName: 'rect', | |
selector: 'r1' | |
}, | |
{ | |
tagName: 'circle', | |
selector: 'c1' | |
}, | |
{ | |
tagName: 'circle', | |
selector: 'c2' | |
}, | |
{ | |
tagName: 'circle', | |
selector: 'c3' | |
}, | |
{ | |
tagName: 'circle', | |
selector: 'c4' | |
} | |
], | |
attrs: { | |
r1: { | |
width: 70, | |
height: 70, | |
stroke: '#ccc', | |
rx: 12, | |
ry: 12, | |
}, | |
c1: { | |
r: 10, | |
cx: 20, | |
cy: 20, | |
fill: '#000' | |
}, | |
c2: { | |
r: 10, | |
cx: 50, | |
cy: 20, | |
fill: '#000' | |
}, | |
c3: { | |
r: 10, | |
cx: 20, | |
cy: 50, | |
fill: '#000' | |
}, | |
c4: { | |
r: 10, | |
cx: 50, | |
cy: 50, | |
fill: '#000' | |
}, | |
} | |
}) |
下面能够看到定义图形的时候,属性是固定写死的,在业务场景中,常常须要动静批改节点的款式,X6 中也提供了十分便当的办法:
const node = graph.addNode({ | |
shape: 'rect', | |
x: 100, | |
y: 100, | |
width: 80, | |
height: 40, | |
attrs: { | |
body: {stroke: 'red'} | |
} | |
}) | |
node.attr('body/stroke', 'green') | |
node.attr('body/fill', 'yellow') |
咱们的图形将会变成上面这样:
那么问题来了,如果多个图形的构造和款式类似度很高,每次定义图形都要写很多相似的代码,有没有一种形式能够将图形之间的公共属性形象进去呢?X6 提供了很优雅的形式来解决这个问题,首先注册自定义的节点类型,在这里配置公共的属性,而后在增加节点的时候指定 shape 值为方才注册的节点类型。
Graph.registerNode('custom-rect', { | |
inherit: 'rect', // 继承自 Shape.Rect | |
width: 300, // 默认宽度 | |
height: 40, // 默认高度 | |
attrs: { | |
body: { | |
rx: 10, // 圆角矩形 | |
ry: 10, | |
strokeWidth: 1, | |
fill: '#5755a1', | |
stroke: '#5755a1', | |
}, | |
label: { | |
fill: '#fff', | |
fontSize: 18, | |
refX: 10, // x 轴偏移,相似 css 中的 margin-left | |
textAnchor: 'left', // 左对齐 | |
} | |
}, | |
}) | |
graph.addNode({ | |
shape: 'custom-rect', | |
x: 50, | |
y: 50, | |
width: 100, | |
height: 50, | |
label: 'rect1' | |
}) | |
graph.addNode({ | |
shape: 'custom-rect', | |
x: 200, | |
y: 50, | |
width: 100, | |
height: 50, | |
label: 'rect2', | |
attrs: { | |
body: {fill: '#ccc'} | |
} | |
}) |
图形拖拽
图形定义好了,接下来咱们要实现图形拖拽性能,X6 提供了 Dnd 插件来提供根底的拖拽能力,并在 Dnd 根底上的进一步封装,提供了一个相似侧边栏的 UI 组件 Stencil,反对分组、折叠、搜寻等能力。
首先提供一个 Stencil 的容器:
<!-- stencil 容器须要设置 position:relative 的款式 --> | |
<div id="stencil" style="position:relative"></div> |
而后进行初始化(具体配置见官网):
const stencil = new Addon.Stencil({ | |
title: 'Flowchart', | |
target: this.graph, | |
stencilGraphWidth: 214, | |
stencilGraphHeight: document.body.offsetHeight - 105, | |
layoutOptions: { | |
columns: 4, | |
columnWidth: 48, | |
rowHeight: 30, | |
marginY: 30, | |
}, | |
}) | |
const stencilContainer = document.querySelector('#stencil') | |
if (stencilContainer) {stencilContainer.appendChild(stencil.container) | |
} |
而后将咱们定义的图形加载到 stencil 中:
const r1 = graph.createNode({ | |
shape: 'rect', | |
width: 30, | |
height: 15, | |
}) | |
stencil.load([r1]) |
当咱们将图形拖拽到画布的时候,想将 Stencil 中的图形等比例放大。查看官网,拖拽的流程为:
- 拖拽过程中能够在 getDragNode 中返回新的节点来自定义拖拽节点款式
- 拖拽完结能够在 getDropNode 中返回新的节点来自定义搁置在画布中节点款式
上面的代码就是将拖拽节点等比例放大 3 倍后搁置到画布中:
const stencil = new Addon.Stencil({getDropNode(node) {const size = node.size() | |
return node.clone().size(size.width * 3, size.height * 3) | |
} | |
}) |
最终成果:
最初
X6 不仅反对上文提到的根底的 SVG 节点,还具备在节点中渲染 React、Vue 组件的能力。在理论业务场景中,如果对 SVG 不相熟或者节点内容简单,咱们能够依据技术栈抉择 React/Vue 渲染,这样在节点外部,咱们能够用相熟的形式绘制各种简单的内容,堪称随心所欲。
- 源码:传送门
- 记得给 X6 仓库加星