Skip to content

Commit bd012e2

Browse files
author
fanyang
committed
feat: snapshot能力增强
1 parent c87a886 commit bd012e2

File tree

7 files changed

+335
-122
lines changed

7 files changed

+335
-122
lines changed

examples/feature-examples/src/pages/extensions/snapshot/data.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ export default {
22
nodes: [
33
{
44
type: 'uml',
5-
x: 100,
6-
y: 100,
5+
x: 0,
6+
y: 0,
77
id: 'uml_1',
88
properties: {
99
name: 'haod',
@@ -12,34 +12,34 @@ export default {
1212
},
1313
{
1414
type: 'rect',
15-
x: 300,
16-
y: 200,
15+
x: 150,
16+
y: 0,
1717
text: {
1818
value: '你好',
19-
x: 300,
20-
y: 200,
19+
x: 150,
20+
y: 0,
2121
},
2222
id: 'rect_1',
2323
},
2424
{
2525
type: 'rect',
26-
x: 500,
27-
y: 300,
26+
x: 400,
27+
y: 100,
2828
text: {
2929
value: '你好2',
30-
x: 500,
31-
y: 300,
30+
x: 400,
31+
y: 100,
3232
},
3333
id: 'rect_2',
3434
},
3535
{
3636
type: 'rect',
37-
x: 700,
38-
y: 300,
37+
x: 300,
38+
y: 200,
3939
text: {
4040
value: '你好3',
41-
x: 700,
42-
y: 300,
41+
x: 300,
42+
y: 200,
4343
},
4444
id: 'rect_3',
4545
},
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#graph {
2-
width: 100%;
3-
height: 70vh;
2+
width: 800px;
3+
height: 500px;
44
}

examples/feature-examples/src/pages/extensions/snapshot/index.tsx

Lines changed: 142 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Card, Divider, Button, Space } from 'antd'
1+
import { Card, Divider, Button, Space, Select, Input } from 'antd'
22
import LogicFlow from '@logicflow/core'
3-
import { useEffect, useRef } from 'react'
4-
import { FlowPath } from '@logicflow/extension'
3+
import { useEffect, useRef, useState } from 'react'
4+
import { Snapshot, DndPanel } from '@logicflow/extension'
55

66
import './index.less'
77
import '@logicflow/core/es/index.css'
@@ -11,17 +11,16 @@ import data from './data'
1111
import Uml from './uml'
1212

1313
const config: Partial<LogicFlow.Options> = {
14-
stopScrollGraph: true,
15-
stopZoomGraph: true,
14+
partial: true,
1615
grid: {
1716
type: 'dot',
1817
size: 20,
1918
},
20-
plugins: [FlowPath],
19+
plugins: [Snapshot, DndPanel],
2120
}
2221

23-
interface Snapshot {
24-
data: LogicFlow.GraphConfigData
22+
interface SnapshotResponse {
23+
data: Blob
2524
width: number
2625
height: number
2726
}
@@ -30,74 +29,163 @@ export default function HighLightExtension() {
3029
const containerRef = useRef<HTMLDivElement | null>(null)
3130
const imgRef = useRef<HTMLImageElement | null>(null)
3231

33-
let lf: LogicFlow
34-
useEffect(() => {
35-
lf = new LogicFlow({
36-
container: containerRef.current!,
37-
...config,
38-
})
39-
40-
lf.register(Uml)
32+
const [fileName, setFileName] = useState<undefined | string>(undefined) // 文件名
33+
const [fileType, setFileType] = useState<string>('png') // 下载的图片类型
34+
const [width, setWidth] = useState<string | undefined>() // 宽度
35+
const [height, setHeight] = useState<string | undefined>(undefined) // 高度
36+
const [padding, setPaddding] = useState<string | undefined>(undefined) //padding
4137

42-
lf.render(data)
43-
lf.translateCenter()
38+
const lfRef = useRef<LogicFlow | null>(null)
39+
useEffect(() => {
40+
if (!lfRef.current) {
41+
lfRef.current = new LogicFlow({
42+
container: containerRef.current!,
43+
...config,
44+
})
45+
46+
lfRef.current.register(Uml)
47+
48+
lfRef.current.setPatternItems([
49+
{
50+
type: 'circle',
51+
text: '开始',
52+
label: '开始节点',
53+
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==',
54+
},
55+
{
56+
type: 'rect',
57+
label: '用户任务',
58+
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
59+
className: 'important-node',
60+
},
61+
{
62+
type: 'rect',
63+
label: '系统任务',
64+
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
65+
className: 'import_icon',
66+
},
67+
{
68+
type: 'diamond',
69+
label: '条件判断',
70+
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII=',
71+
},
72+
{
73+
type: 'circle',
74+
text: '结束',
75+
label: '结束节点',
76+
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC',
77+
},
78+
])
79+
lfRef.current.render(data)
80+
lfRef.current.translateCenter()
81+
82+
lfRef.current.extension.snapshot.useGlobalRules = false
83+
lfRef.current.extension.snapshot.customCssRules = `
84+
.uml-wrapper {
85+
line-height: 1.2;
86+
text-align: center;
87+
color: blue;
88+
}
89+
`
90+
}
4491
}, [])
4592

93+
// 下载
4694
const downLoad = () => {
47-
lf.getSnapshot()
95+
lfRef.current &&
96+
lfRef.current.getSnapshot(fileName, {
97+
fileType,
98+
height: height,
99+
padding: padding,
100+
backgroundColor: 'yellow',
101+
partialElement: true,
102+
...(width ? { width } : {}),
103+
...(height ? { height } : {}),
104+
...(padding ? { padding } : {}),
105+
})
48106
}
49107

108+
// 预览
50109
const preview = () => {
51-
lf.getSnapshotBlob('#FFFFFF').then(({ data, width, height }: Snapshot) => {
52-
// imgRef.current && imgRef.current.src = window.URL.createObjectURL(data);
53-
console.log(width, height, data)
54-
})
110+
lfRef.current &&
111+
lfRef.current
112+
.getSnapshotBlob('#FFFFFF', fileType)
113+
.then(({ data }: SnapshotResponse) => {
114+
if (imgRef.current) {
115+
imgRef.current.width = 500
116+
imgRef.current.height = 400
117+
imgRef.current.src = window.URL.createObjectURL(data)
118+
}
119+
})
55120
}
56121

122+
// 打印base64地址
57123
const logBase64 = () => {
58-
lf.getSnapshotBase64().then(({ data, width, height }: Snapshot) => {
59-
// document.getElementById('img').src = data
60-
console.log(width, height, data)
61-
})
62-
}
63-
64-
const downloadXml = () => {
65-
const data = lf.getGraphData()
66-
console.log(lfJson2Xml(data))
67-
download('logicflow.xml', lfJson2Xml(data))
68-
69-
function download(filename, text) {
70-
const element = document.createElement('a')
71-
element.setAttribute(
72-
'href',
73-
'data:text/plain;charset=utf-8,' + encodeURIComponent(text),
74-
)
75-
element.setAttribute('download', filename)
76-
77-
element.style.display = 'none'
78-
document.body.appendChild(element)
79-
80-
element.click()
81-
82-
document.body.removeChild(element)
83-
}
124+
lfRef.current &&
125+
lfRef.current
126+
.getSnapshotBase64(undefined, fileType)
127+
.then(({ data, width, height }: SnapshotResponse) => {
128+
// document.getElementById('img').src = data
129+
console.log(width, height, data)
130+
})
84131
}
85132

86133
return (
87134
<Card title="LogicFlow Extension - Snapshot">
135+
<div style={{ marginBottom: '10px' }}>
136+
<Space>
137+
<span>文件名:</span>
138+
<Input
139+
placeholder="文件名"
140+
value={fileName}
141+
onChange={(e) => setFileName(e.target.value)}
142+
/>
143+
<span>文件类型(默认png):</span>
144+
<Select
145+
defaultValue={fileType}
146+
style={{ width: 120 }}
147+
onChange={(value) => setFileType(value)}
148+
options={[
149+
{ value: 'png', label: 'png' },
150+
{ value: 'jpeg', label: 'jpeg' },
151+
{ value: 'webp', label: 'webp' },
152+
{ value: 'gif', label: 'gif' },
153+
]}
154+
/>
155+
<span>宽度</span>
156+
<Input
157+
placeholder="width"
158+
value={width}
159+
onChange={(e) => setWidth(e.target.value)}
160+
/>
161+
<span>高度</span>
162+
<Input
163+
placeholder="height"
164+
value={height}
165+
onChange={(e) => setHeight(e.target.value)}
166+
/>
167+
<span>padding</span>
168+
<Input
169+
placeholder="padding"
170+
value={padding}
171+
onChange={(e) => setPaddding(e.target.value)}
172+
/>
173+
<Button id="download" onClick={downLoad}>
174+
下载快照
175+
</Button>
176+
</Space>
177+
</div>
178+
<Divider />
88179
<Space>
89-
<Button id="download" onClick={downLoad}>
90-
下载快照
91-
</Button>
92180
<Button id="preview" onClick={preview}>
93181
预览
94182
</Button>
95183
<Button id="base64" onClick={logBase64}>
96184
打印base64
97185
</Button>
98-
<Button id="downloadXml" onClick={downloadXml}>
186+
{/* <Button id="downloadXml" onClick={downloadXml}>
99187
获取xml
100-
</Button>
188+
</Button> */}
101189
</Space>
102190
<Divider />
103191
<div ref={containerRef} id="graph"></div>

packages/core/src/model/GraphModel.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,21 @@ export class GraphModel {
14841484
const edgeModel = this.getEdgeModelById(edgeId)
14851485
edgeModel?.closeEdgeAnimation()
14861486
}
1487+
1488+
/**
1489+
* 设置是否开启局部渲染模式,返回重新渲染后promise
1490+
* @param partial boolean
1491+
* @returns Promise<boolean>
1492+
*/
1493+
@action setPartial(partial: boolean): Promise<boolean> {
1494+
this.partial = partial
1495+
return new Promise((resolve, reject) => {
1496+
this.eventCenter.on('graph: partialRendered', ({ isRendered }) => {
1497+
isRendered ? resolve(isRendered) : reject(isRendered)
1498+
this.eventCenter.off('graph: partialRendered')
1499+
})
1500+
})
1501+
}
14871502
}
14881503

14891504
export namespace GraphModel {

packages/core/src/view/Graph.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class Graph extends Component<IGraphProps> {
7171
const grid = options.grid && Grid.getGridOptions(options.grid)
7272
const { fakeNode, editConfigModel } = graphModel
7373
const { adjustEdge } = editConfigModel
74-
74+
graphModel.eventCenter.emit('graph: partialRendered', { isRendered: true })
7575
return (
7676
<div className="lf-graph" flow-id={graphModel.flowId} style={style}>
7777
<CanvasOverlay graphModel={graphModel} dnd={dnd}>

packages/extension/src/tools/snapshot/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Snapshot 导出图片插件
2+
13
## 截取logic-flow内部图形保存为文件并下载
24

35
## 往logicflow上下文中添加api getSnapshot

0 commit comments

Comments
 (0)