Skip to content

Commit 2f58671

Browse files
authored
docs: add snake flow diagram (#6581)
* docs: add snake flow diagram * fix: snake polyline
1 parent 3ef6d94 commit 2f58671

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

packages/site/examples/scene-case/default/demo/meta.json

+8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@
5959
"en": "Why Do Cats?"
6060
},
6161
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ug4vTJA7QbMAAAAAAAAAAAAADmJ7AQ/original"
62+
},
63+
{
64+
"filename": "snake-flow-diagram.js",
65+
"title": {
66+
"zh": "S 型流程图",
67+
"en": "Snake Flow Diagram"
68+
},
69+
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*7L51SLW2WhcAAAAAAAAAAAAADmJ7AQ/original"
6270
}
6371
]
6472
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { BaseLayout, ExtensionCategory, Graph, Polyline, positionOf, register } from '@antv/g6';
2+
3+
const data = {
4+
nodes: [
5+
{ id: '0', data: { label: '开始流程', time: '17:00:00' } },
6+
{ id: '1', data: { label: '流程1', time: '17:00:05' } },
7+
{ id: '2', data: { label: '流程2', time: '17:00:12' } },
8+
{ id: '3', data: { label: '流程3', time: '17:00:30' } },
9+
{ id: '4', data: { label: '流程4', time: '17:02:00' } },
10+
{ id: '5', data: { label: '流程5', time: '17:02:40' } },
11+
{ id: '6', data: { label: '流程6', time: '17:05:50' } },
12+
{ id: '7', data: { label: '流程7', time: '17:10:00' } },
13+
{ id: '8', data: { label: '流程8', time: '17:11:20' } },
14+
{ id: '9', data: { label: '流程9', time: '17:15:00' } },
15+
{ id: '10', data: { label: '流程10', time: '17:30:00' } },
16+
{ id: '11', data: { label: '流程11' } },
17+
{ id: '12', data: { label: '流程12' } },
18+
{ id: '13', data: { label: '流程13' } },
19+
{ id: '14', data: { label: '流程14' } },
20+
{ id: '15', data: { label: '流程结束' } },
21+
],
22+
edges: [
23+
{ source: '0', target: '1', data: { done: true } },
24+
{ source: '1', target: '2', data: { done: true } },
25+
{ source: '2', target: '3', data: { done: true } },
26+
{ source: '3', target: '4', data: { done: true } },
27+
{ source: '4', target: '5', data: { done: true } },
28+
{ source: '5', target: '6', data: { done: true } },
29+
{ source: '6', target: '7', data: { done: true } },
30+
{ source: '7', target: '8', data: { done: true } },
31+
{ source: '8', target: '9', data: { done: true } },
32+
{ source: '9', target: '10', data: { done: true } },
33+
{ source: '10', target: '11', data: { done: false } },
34+
{ source: '11', target: '12', data: { done: false } },
35+
{ source: '12', target: '13', data: { done: false } },
36+
{ source: '13', target: '14', data: { done: false } },
37+
{ source: '14', target: '15', data: { done: false } },
38+
],
39+
};
40+
41+
class SnakeLayout extends BaseLayout {
42+
id = 's-layout';
43+
44+
async execute(data, options) {
45+
const {
46+
width,
47+
height,
48+
nodeSize = 32,
49+
cols = 5,
50+
sep: propSep,
51+
nodeSep: propNodeSep,
52+
} = { ...this.options, ...options };
53+
54+
const totalRows = Math.ceil(data.nodes.length / cols);
55+
const sep = propSep ? propSep : height / (totalRows - 1) - nodeSize;
56+
const nodeSep = propNodeSep ? propNodeSep : width / cols - nodeSize;
57+
58+
const nodes = data.nodes.map((node, index) => {
59+
const row = Math.floor(index / cols);
60+
const col = index % cols;
61+
const x = (col + 0.5) * (nodeSize + nodeSep);
62+
63+
let y = row * (nodeSize + sep);
64+
if (row === 0) y += nodeSize / 2;
65+
if (row === totalRows - 1) y -= nodeSize / 2;
66+
67+
const adjustedX = row % 2 === 0 ? x : (cols - col - 0.5) * (nodeSize + nodeSep);
68+
69+
return {
70+
id: node.id,
71+
style: { x: adjustedX, y },
72+
};
73+
});
74+
75+
return { nodes };
76+
}
77+
}
78+
79+
class SnakePolyline extends Polyline {
80+
getPoints(attributes) {
81+
const [sourcePoint, targetPoint] = this.getEndpoints(attributes, false);
82+
83+
if (sourcePoint[1] === targetPoint[1]) return [sourcePoint, targetPoint];
84+
85+
const prevPointId = this.context.model
86+
.getRelatedEdgesData(this.sourceNode.id)
87+
.filter((edge) => edge.target === this.sourceNode.id)[0]?.source;
88+
if (!prevPointId) return [sourcePoint, targetPoint];
89+
90+
const prevPoint = positionOf(this.context.model.getNodeLikeDatum(prevPointId));
91+
const offset = -(prevPoint[0] - sourcePoint[0]) / 2;
92+
return [
93+
sourcePoint,
94+
[sourcePoint[0] + offset, sourcePoint[1]],
95+
[targetPoint[0] + offset, targetPoint[1]],
96+
targetPoint,
97+
];
98+
}
99+
}
100+
101+
register(ExtensionCategory.LAYOUT, 's-layout', SnakeLayout);
102+
register(ExtensionCategory.EDGE, 's-polyline', SnakePolyline);
103+
104+
const graph = new Graph({
105+
container: 'container',
106+
data,
107+
animation: false,
108+
background: '#fafafa',
109+
autoFit: 'center',
110+
node: {
111+
style: {
112+
fill: (d) => (d.data.time ? '#1783ff' : '#d9d9d9'),
113+
lineWidth: 2,
114+
size: 8,
115+
stroke: (d) => (d.data.time ? 'lightblue' : ''),
116+
labelFontWeight: 500,
117+
labelOffsetY: 8,
118+
labelText: (d) => d.data.label,
119+
badge: true,
120+
badges: (d) => [
121+
{
122+
background: false,
123+
fill: '#858ca6',
124+
fontSize: 10,
125+
offsetY: 39,
126+
placement: 'bottom',
127+
text: d.data.time || '--',
128+
},
129+
],
130+
},
131+
},
132+
edge: {
133+
type: 's-polyline',
134+
style: {
135+
lineWidth: 2,
136+
stroke: (d) => (d.data.done ? '#1783ff' : '#d9d9d9'),
137+
},
138+
},
139+
layout: {
140+
type: 's-layout',
141+
cols: 6,
142+
sep: 120,
143+
nodeSep: 80,
144+
nodeSize: 32,
145+
},
146+
behaviors: ['drag-canvas', 'zoom-canvas'],
147+
});
148+
149+
graph.render();

0 commit comments

Comments
 (0)