From 2f58671ddd8162353c246c2946dcf28341eeadb8 Mon Sep 17 00:00:00 2001 From: Yuxin <55794321+yvonneyx@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:42:59 +0800 Subject: [PATCH] docs: add snake flow diagram (#6581) * docs: add snake flow diagram * fix: snake polyline --- .../scene-case/default/demo/meta.json | 8 + .../default/demo/snake-flow-diagram.js | 149 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 packages/site/examples/scene-case/default/demo/snake-flow-diagram.js diff --git a/packages/site/examples/scene-case/default/demo/meta.json b/packages/site/examples/scene-case/default/demo/meta.json index 5171680dbba..eb5a523b025 100644 --- a/packages/site/examples/scene-case/default/demo/meta.json +++ b/packages/site/examples/scene-case/default/demo/meta.json @@ -59,6 +59,14 @@ "en": "Why Do Cats?" }, "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ug4vTJA7QbMAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "snake-flow-diagram.js", + "title": { + "zh": "S 型流程图", + "en": "Snake Flow Diagram" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*7L51SLW2WhcAAAAAAAAAAAAADmJ7AQ/original" } ] } diff --git a/packages/site/examples/scene-case/default/demo/snake-flow-diagram.js b/packages/site/examples/scene-case/default/demo/snake-flow-diagram.js new file mode 100644 index 00000000000..1ab80972ff2 --- /dev/null +++ b/packages/site/examples/scene-case/default/demo/snake-flow-diagram.js @@ -0,0 +1,149 @@ +import { BaseLayout, ExtensionCategory, Graph, Polyline, positionOf, register } from '@antv/g6'; + +const data = { + nodes: [ + { id: '0', data: { label: '开始流程', time: '17:00:00' } }, + { id: '1', data: { label: '流程1', time: '17:00:05' } }, + { id: '2', data: { label: '流程2', time: '17:00:12' } }, + { id: '3', data: { label: '流程3', time: '17:00:30' } }, + { id: '4', data: { label: '流程4', time: '17:02:00' } }, + { id: '5', data: { label: '流程5', time: '17:02:40' } }, + { id: '6', data: { label: '流程6', time: '17:05:50' } }, + { id: '7', data: { label: '流程7', time: '17:10:00' } }, + { id: '8', data: { label: '流程8', time: '17:11:20' } }, + { id: '9', data: { label: '流程9', time: '17:15:00' } }, + { id: '10', data: { label: '流程10', time: '17:30:00' } }, + { id: '11', data: { label: '流程11' } }, + { id: '12', data: { label: '流程12' } }, + { id: '13', data: { label: '流程13' } }, + { id: '14', data: { label: '流程14' } }, + { id: '15', data: { label: '流程结束' } }, + ], + edges: [ + { source: '0', target: '1', data: { done: true } }, + { source: '1', target: '2', data: { done: true } }, + { source: '2', target: '3', data: { done: true } }, + { source: '3', target: '4', data: { done: true } }, + { source: '4', target: '5', data: { done: true } }, + { source: '5', target: '6', data: { done: true } }, + { source: '6', target: '7', data: { done: true } }, + { source: '7', target: '8', data: { done: true } }, + { source: '8', target: '9', data: { done: true } }, + { source: '9', target: '10', data: { done: true } }, + { source: '10', target: '11', data: { done: false } }, + { source: '11', target: '12', data: { done: false } }, + { source: '12', target: '13', data: { done: false } }, + { source: '13', target: '14', data: { done: false } }, + { source: '14', target: '15', data: { done: false } }, + ], +}; + +class SnakeLayout extends BaseLayout { + id = 's-layout'; + + async execute(data, options) { + const { + width, + height, + nodeSize = 32, + cols = 5, + sep: propSep, + nodeSep: propNodeSep, + } = { ...this.options, ...options }; + + const totalRows = Math.ceil(data.nodes.length / cols); + const sep = propSep ? propSep : height / (totalRows - 1) - nodeSize; + const nodeSep = propNodeSep ? propNodeSep : width / cols - nodeSize; + + const nodes = data.nodes.map((node, index) => { + const row = Math.floor(index / cols); + const col = index % cols; + const x = (col + 0.5) * (nodeSize + nodeSep); + + let y = row * (nodeSize + sep); + if (row === 0) y += nodeSize / 2; + if (row === totalRows - 1) y -= nodeSize / 2; + + const adjustedX = row % 2 === 0 ? x : (cols - col - 0.5) * (nodeSize + nodeSep); + + return { + id: node.id, + style: { x: adjustedX, y }, + }; + }); + + return { nodes }; + } +} + +class SnakePolyline extends Polyline { + getPoints(attributes) { + const [sourcePoint, targetPoint] = this.getEndpoints(attributes, false); + + if (sourcePoint[1] === targetPoint[1]) return [sourcePoint, targetPoint]; + + const prevPointId = this.context.model + .getRelatedEdgesData(this.sourceNode.id) + .filter((edge) => edge.target === this.sourceNode.id)[0]?.source; + if (!prevPointId) return [sourcePoint, targetPoint]; + + const prevPoint = positionOf(this.context.model.getNodeLikeDatum(prevPointId)); + const offset = -(prevPoint[0] - sourcePoint[0]) / 2; + return [ + sourcePoint, + [sourcePoint[0] + offset, sourcePoint[1]], + [targetPoint[0] + offset, targetPoint[1]], + targetPoint, + ]; + } +} + +register(ExtensionCategory.LAYOUT, 's-layout', SnakeLayout); +register(ExtensionCategory.EDGE, 's-polyline', SnakePolyline); + +const graph = new Graph({ + container: 'container', + data, + animation: false, + background: '#fafafa', + autoFit: 'center', + node: { + style: { + fill: (d) => (d.data.time ? '#1783ff' : '#d9d9d9'), + lineWidth: 2, + size: 8, + stroke: (d) => (d.data.time ? 'lightblue' : ''), + labelFontWeight: 500, + labelOffsetY: 8, + labelText: (d) => d.data.label, + badge: true, + badges: (d) => [ + { + background: false, + fill: '#858ca6', + fontSize: 10, + offsetY: 39, + placement: 'bottom', + text: d.data.time || '--', + }, + ], + }, + }, + edge: { + type: 's-polyline', + style: { + lineWidth: 2, + stroke: (d) => (d.data.done ? '#1783ff' : '#d9d9d9'), + }, + }, + layout: { + type: 's-layout', + cols: 6, + sep: 120, + nodeSep: 80, + nodeSize: 32, + }, + behaviors: ['drag-canvas', 'zoom-canvas'], +}); + +graph.render();