begin
In the flowchart, we need to add nodes to the canvas through drag-and-drop interaction. X6 not only has a built-in powerful drag-and-drop function, but also a lot of commonly used graphics. Next, we will implement the basic flowchart graphics and graphic drag-and-drop functions together.
achieve
Graphic definition
First look at the basic configuration of the next simple rectangular node:
graph.addNode({
shape: 'rect',
x: 100,
y: 100,
width: 80,
height: 40,
attrs: {
body: {
stroke: 'red'
}
}
})
shape: Define the shape of the graphic. X6 has built-in basic shapes such as rect, circle, ellipse, polygon, polyline, image, html, etc.
x/y: define the coordinates of the upper left corner of the graph
width/height: defines the size of the graphic
It may be strange to see attrs. What is this? In fact, attrs can be regarded as a collection of css styles, where body is similar to a css selector, and the value of body is an attribute of the selected element. Where did the body come from? Here is another important configuration item markup. Markup represents the DOM structure of graphics. The default markup of the built-in rect is:
[
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'text',
selector: 'label',
},
]
After rendering, the actual effective DOM is:
<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>
Therefore, a graph is determined by markup and attrs to determine the structure and style. We can define the graph in our business by setting markup and attrs. See the following example:
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'
},
}
})
As you can see above, when defining the graph, the attributes are fixed and hard-coded. In business scenarios, it is often necessary to dynamically modify the style of the node. X6 also provides a very convenient method:
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')
Our graph will look like this:
So the question is, if the structure and style of multiple graphics are very similar, a lot of similar codes must be written every time a graphic is defined. Is there a way to abstract the common attributes between the graphics? X6 provides a very elegant way to solve this problem. First, register the custom node type, configure the public attributes here, and then specify the shape value when adding the node to the node type just registered.
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'
}
}
})
Graphic drag and drop
The graphics are defined. Next, we need to implement the graphics drag-and-drop function. X6 provides the Dnd plug-in to provide basic drag-and-drop capabilities, and further package on the basis of Dnd, providing a UI component similar to the sidebar Stencil , Supports grouping, folding, searching and other capabilities.
First provide a Stencil container:
<!-- stencil 容器需要设置 position:relative 的样式 -->
<div id="stencil" style="position:relative"></div>
Then initialize (see official website specific configuration):
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)
}
Then load the graphics we defined into stencil:
const r1 = graph.createNode({
shape: 'rect',
width: 30,
height: 15,
})
stencil.load([r1])
When we drag the graphics to the canvas, we want to enlarge the graphics in Stencil in equal proportions. Check the official website, the drag and drop process is:
- You can return new nodes in getDragNode during the dragging process to customize the drag node style
- After dragging, you can return a new node in getDropNode to customize the style of the node placed in the canvas
The following code is to enlarge the drag node by 3 times and place it on the canvas:
const stencil = new Addon.Stencil({
getDropNode(node) {
const size = node.size()
return node.clone().size(size.width * 3, size.height * 3)
}
})
final effect:
At last
X6 not only supports the basic SVG nodes mentioned above, but also has the ability to render React and Vue components in the nodes. In actual business scenarios, if you are not familiar with SVG or the content of the node is complex, we can choose React/Vue to render according to the technology stack, so that inside the node, we can draw various complex content in a familiar way, which can be described as doing whatever we want.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。