Skip to content

Commit 62a30bb

Browse files
DymoneLewisboyongjiong
authored andcommitted
feat: 画布导出的安全系数和安全边距支持配置 fix #2213
1 parent 58fe653 commit 62a30bb

File tree

6 files changed

+262
-72
lines changed

6 files changed

+262
-72
lines changed

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

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,20 @@ export default function SnapshotExample() {
7676
const [quality, setQuality] = useState<number>() // 图片质量
7777
const [partial, setPartial] = useState<boolean>(false) // 导出局部渲染
7878

79+
// 快照插件样式控制
80+
const [useGlobalRules, setUseGlobalRules] = useState<boolean>(true) // 是否注入全局样式
81+
const [customCssRules, setCustomCssRules] = useState<string>(`
82+
.uml-wrapper {
83+
line-height: 1.2;
84+
text-align: center;
85+
color: blue;
86+
}
87+
`) // 自定义样式规则,将叠加到导出图片中
88+
89+
// 画布尺寸安全参数
90+
const [safetyFactor, setSafetyFactor] = useState<number>(1.1) // 画布导出安全系数
91+
const [safetyMargin, setSafetyMargin] = useState<number>(40) // 画布导出安全边距
92+
7993
const [blobData, setBlobData] = useState('')
8094
const [base64Data, setBase64Data] = useState('')
8195

@@ -118,16 +132,12 @@ export default function SnapshotExample() {
118132
})
119133
})
120134

121-
// 默认开启css样式
122-
lf.extension.snapshot.useGlobalRules = true
123-
// 不会覆盖css样式,会叠加,customCssRules优先级高
124-
lf.extension.snapshot.customCssRules = `
125-
.uml-wrapper {
126-
line-height: 1.2;
127-
text-align: center;
128-
color: blue;
129-
}
130-
`
135+
// 设置快照插件样式参数(通过类型断言访问扩展实例属性)
136+
const snapshotExt = lf.extension?.snapshot as unknown as Snapshot
137+
if (snapshotExt) {
138+
snapshotExt.useGlobalRules = useGlobalRules
139+
snapshotExt.customCssRules = customCssRules
140+
}
131141

132142
lf.render(data)
133143
lf.translateCenter()
@@ -146,8 +156,17 @@ export default function SnapshotExample() {
146156
height,
147157
padding,
148158
quality,
159+
safetyFactor,
160+
safetyMargin,
149161
}
150162
console.log(params, 'params')
163+
// 在导出前更新快照扩展的样式控制参数
164+
const snapshotExt = lfRef.current?.extension
165+
?.snapshot as unknown as Snapshot
166+
if (snapshotExt) {
167+
snapshotExt.useGlobalRules = useGlobalRules
168+
snapshotExt.customCssRules = customCssRules
169+
}
151170
await lfRef.current?.getSnapshot(fileName, params)
152171
// await lfRef.current?.extension.snapshot?.getSnapshot(fileName, params)
153172
// 测试
@@ -172,6 +191,15 @@ export default function SnapshotExample() {
172191
height,
173192
padding,
174193
quality,
194+
safetyFactor,
195+
safetyMargin,
196+
}
197+
// 在预览前更新快照扩展的样式控制参数
198+
const snapshotExt = lfRef.current.extension
199+
?.snapshot as unknown as Snapshot
200+
if (snapshotExt) {
201+
snapshotExt.useGlobalRules = useGlobalRules
202+
snapshotExt.customCssRules = customCssRules
175203
}
176204
lfRef.current
177205
.getSnapshotBlob(backgroundColor, fileType, params)
@@ -204,6 +232,15 @@ export default function SnapshotExample() {
204232
height,
205233
padding,
206234
quality,
235+
safetyFactor,
236+
safetyMargin,
237+
}
238+
// 在预览前更新快照扩展的样式控制参数
239+
const snapshotExt = lfRef.current.extension
240+
?.snapshot as unknown as Snapshot
241+
if (snapshotExt) {
242+
snapshotExt.useGlobalRules = useGlobalRules
243+
snapshotExt.customCssRules = customCssRules
207244
}
208245
const result = await lfRef.current.getSnapshotBase64(
209246
'white',
@@ -287,6 +324,16 @@ export default function SnapshotExample() {
287324
value={height}
288325
onChange={(value) => handleInputChange(value, 'height')}
289326
/>
327+
<InputNumber
328+
addonBefore="安全系数:"
329+
value={safetyFactor}
330+
onChange={(value) => setSafetyFactor(value ?? 1.1)}
331+
/>
332+
<InputNumber
333+
addonBefore="安全边距:"
334+
value={safetyMargin}
335+
onChange={(value) => setSafetyMargin(value ?? 40)}
336+
/>
290337
</Space>
291338
<p></p>
292339
<Space>
@@ -307,6 +354,20 @@ export default function SnapshotExample() {
307354
/>
308355
<span>导出局部渲染:</span>
309356
<Switch onChange={(partial) => setPartial(partial)} />
357+
<span>注入全局样式:</span>
358+
<Switch
359+
checked={useGlobalRules}
360+
onChange={(checked) => setUseGlobalRules(checked)}
361+
/>
362+
</Space>
363+
<p></p>
364+
<Space style={{ width: '100%' }} direction="vertical">
365+
<Input.TextArea
366+
rows={4}
367+
placeholder="自定义导出样式,支持标准CSS语法"
368+
value={customCssRules}
369+
onChange={(e) => setCustomCssRules(e.target.value)}
370+
/>
310371
</Space>
311372
<Divider />
312373
<Space>

packages/extension/src/tools/snapshot/index.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ export type ToImageOptions = {
3535
* - `true`:只导出画面区域内的可见元素
3636
*/
3737
partial?: boolean
38+
/**
39+
* 导出图片时的安全系数,用于确保导出的图片能够容纳所有元素,默认值为 1.1
40+
*/
41+
safetyFactor?: number
42+
/**
43+
* 导出图片时的安全边距,用于确保导出的图片能够容纳所有元素,默认值为 40
44+
*/
45+
safetyMargin?: number
3846
}
3947

4048
// Blob | base64
@@ -381,7 +389,7 @@ export class Snapshot {
381389
// 计算实际宽高,考虑缩放因素
382390
// 在宽画布情况下,getBoundingClientRect可能无法获取到所有元素的边界
383391
// 因此我们添加一个安全系数来确保能够容纳所有元素
384-
const safetyFactor = 1.1 // 安全系数,增加20%的空间
392+
const safetyFactor = toImageOptions.safetyFactor || 1.1 // 安全系数,增加10%的空间
385393
const actualWidth = (bbox.width / SCALE_X) * safetyFactor
386394
const actualHeight = (bbox.height / SCALE_Y) * safetyFactor
387395

@@ -394,7 +402,7 @@ export class Snapshot {
394402

395403
// 宽高值 默认加padding 40,保证图形不会紧贴着下载图片
396404
// 为宽画布添加额外的安全边距,确保不会裁剪
397-
const safetyMargin = 40 // 额外的安全边距
405+
const safetyMargin = toImageOptions.safetyMargin || 40 // 额外的安全边距
398406

399407
// 获取当前浏览器类型,不同浏览器对canvas的限制不同
400408
const { maxCanvasDimension, otherMaxCanvasDimension } =

sites/docs/docs/tutorial/extension/snapshot.en.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ group:
66
title: Snapshot
77
order: 6
88
toc: content
9-
tag: Optimization
109
---
1110

1211
We often need to export the canvas content as an image. LogicFlow provides an independent plug-in package `Snapshot` to support exporting the canvas as an image.
@@ -53,15 +52,17 @@ In version 2.0, we have comprehensively upgraded the export functionality:
5352

5453
The export method supports the `toImageOptions` parameter with the following configuration options:
5554

56-
| Property Name | Type | Default Value | Description |
57-
| ------------- | ---- | ------------- | ----------- |
58-
| fileType | string | png | Export format: `png`, `webp`, `jpeg`, `svg` |
59-
| width | number | - | Image width (may cause image stretching) |
60-
| height | number | - | Image height (may cause image stretching) |
61-
| backgroundColor | string | - | Background color, transparent by default |
62-
| quality | number | 0.92 | Image quality, only effective for `jpeg` and `webp`, value range 0-1 |
63-
| padding | number | 40 | Inner margin, in pixels |
64-
| partial | boolean | false | Whether to export only the visible area |
55+
| Property Name | Type | Default Value | Description |
56+
| --------------- | ------- | ------------- | ----------------------------------------------------------------------------------------------------------------------- |
57+
| fileType | string | png | Export format: `png`, `webp`, `jpeg`, `svg` |
58+
| width | number | - | Image width (may cause image stretching) |
59+
| height | number | - | Image height (may cause image stretching) |
60+
| backgroundColor | string | - | Background color, transparent by default |
61+
| quality | number | 0.92 | Image quality, only effective for `jpeg` and `webp`, value range 0-1 |
62+
| padding | number | 40 | Inner margin, in pixels |
63+
| partial | boolean | false | Whether to export only the visible area |
64+
| safetyFactor | number | 1.1 | Safety factor: for wide canvas scenarios, enlarges the export bounds proportionally to ensure all elements are included |
65+
| safetyMargin | number | 40 | Safety margin: extra margin for wide canvas scenarios to avoid cropping |
6566

6667
:::warning{title=Notes}
6768
- SVG format does not support `width`, `height`, `backgroundColor`, `padding` attributes
@@ -70,6 +71,9 @@ The export method supports the `toImageOptions` parameter with the following con
7071
- During export, silent mode will be automatically enabled, disabling canvas interaction
7172
- Automatically converts relative path images in SVG to Base64 encoding <Badge type="warning">2.0.14 New</Badge>
7273
- When the image exceeds the browser's canvas limit, it will automatically scale the image size to ensure successful export, but it will affect image clarity
74+
- You can fine-tune wide canvas behavior via `safetyFactor` and `safetyMargin` to avoid element cropping
75+
- If `partial` is not explicitly provided, it defaults to the current canvas partial rendering state; during export, the rendering mode may be temporarily switched and will be restored afterward
76+
- Anchors and rotate controls are automatically removed during export to prevent auxiliary elements from appearing in the image
7377
:::
7478

7579
### Custom CSS Styles

sites/docs/docs/tutorial/extension/snapshot.zh.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ group:
66
title: 导出图片 (Snapshot)
77
order: 6
88
toc: content
9-
tag: 优化
109
---
1110

1211
我们常常有需要将画布内容以图片的形式导出来的情况,因此LogicFlow提供了一个独立的插件包 `Snapshot` 以支持用户将画布导出为图片。
@@ -52,15 +51,17 @@ lf.getSnapshot('流程图');
5251

5352
导出方法支持`toImageOptions`参数,提供以下配置项:
5453

55-
| 属性名 | 类型 | 默认值 | 描述 |
56-
| --------------- | ------- | ------ | ----------------------------------------- |
57-
| fileType | string | png | 导出格式:`png``webp``jpeg``svg` |
58-
| width | number | - | 图片宽度(可能导致图形拉伸) |
59-
| height | number | - | 图片高度(可能导致图形拉伸) |
60-
| backgroundColor | string | - | 背景色,默认透明 |
61-
| quality | number | 0.92 | 图片质量,仅对`jpeg``webp`有效,取值0-1 |
62-
| padding | number | 40 | 内边距,单位像素 |
63-
| partial | boolean | false | 是否只导出可见区域 |
54+
| 属性名 | 类型 | 默认值 | 描述 |
55+
| --------------- | ------- | ------ | ---------------------------------------------------------------------- |
56+
| fileType | string | png | 导出格式:`png``webp``jpeg``svg` |
57+
| width | number | - | 图片宽度(可能导致图形拉伸) |
58+
| height | number | - | 图片高度(可能导致图形拉伸) |
59+
| backgroundColor | string | - | 背景色,默认透明 |
60+
| quality | number | 0.92 | 图片质量,仅对`jpeg``webp`有效,取值0-1 |
61+
| padding | number | 40 | 内边距,单位像素 |
62+
| partial | boolean | false | 是否只导出可见区域 |
63+
| safetyFactor | number | 1.1 | 安全系数:用于宽画布场景,导出时按比例扩大画布边界,确保所有元素被包含 |
64+
| safetyMargin | number | 40 | 安全边距:用于宽画布场景,导出时额外增加边距,避免被裁剪 |
6465

6566
:::warning{title=注意事项}
6667
- 导出SVG格式的图片时不支持`width``height``backgroundColor``padding`属性
@@ -69,6 +70,9 @@ lf.getSnapshot('流程图');
6970
- 导出过程中会自动开启静默模式,禁用画布交互
7071
- 自动将SVG中的相对路径图片转换为Base64编码<Badge type="warning">2.0.14新增</Badge>
7172
- 导出图片超过浏览器对canvas限制时,会自动缩放图片尺寸,确保导出成功,但会影响图片清晰度
73+
- 可通过 `safetyFactor``safetyMargin` 精细调整宽画布的安全余量,避免元素裁剪
74+
- `partial` 未显式传入时,默认沿用当前画布的局部渲染状态;导出期间如需切换渲染模式会临时切换并在导出完成后还原
75+
- 导出时会自动移除锚点与旋转控件,避免辅助元素进入图片
7276
:::
7377

7478
### 自定义CSS样式

sites/docs/examples/extension/native/demo/snapshot.tsx

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React, { useEffect, useRef, useState } from 'react';
2+
3+
declare const insertCss: (css: string) => void;
24
import {
35
Button,
46
Col,
@@ -222,7 +224,19 @@ const SnapshotExample: React.FC = () => {
222224
const [backgroundColor, setBackgroundColor] = useState<string>('white'); // 背景颜色
223225
const [padding, setPadding] = useState<number>(); //padding
224226
const [quality, setQuality] = useState<number>(); // 图片质量
225-
const [partial, setPartial] = useState<boolean>(true); // 导出局部渲染
227+
const [partial, setPartial] = useState<boolean>(false); // 导出局部渲染
228+
229+
// 快照插件样式控制
230+
const [useGlobalRules, setUseGlobalRules] = useState<boolean>(true); // 是否注入全局样式
231+
const [customCssRules, setCustomCssRules] = useState<string>(`.uml-wrapper {
232+
line-height: 1.2;
233+
text-align: center;
234+
color: blue;
235+
}`); // 自定义样式规则,将叠加到导出图片中
236+
237+
// 画布尺寸安全参数
238+
const [safetyFactor, setSafetyFactor] = useState<number>(1.1); // 画布导出安全系数
239+
const [safetyMargin, setSafetyMargin] = useState<number>(40); // 画布导出安全边距
226240

227241
const [blobData, setBlobData] = useState('');
228242
const [base64Data, setBase64Data] = useState('');
@@ -259,16 +273,12 @@ const SnapshotExample: React.FC = () => {
259273
});
260274
});
261275

262-
// 默认开启css样式
263-
lf.extension.snapshot.useGlobalRules = true;
264-
// 不会覆盖css样式,会叠加,customCssRules优先级高
265-
lf.extension.snapshot.customCssRules = `
266-
.uml-wrapper {
267-
line-height: 1.2;
268-
text-align: center;
269-
color: blue;
270-
}
271-
`;
276+
// 设置快照插件样式参数(通过类型断言访问扩展实例属性)
277+
const snapshotExt = lf.extension?.snapshot;
278+
if (snapshotExt) {
279+
snapshotExt.useGlobalRules = useGlobalRules;
280+
snapshotExt.customCssRules = customCssRules;
281+
}
272282

273283
lf.render(data);
274284
lf.translateCenter();
@@ -287,8 +297,16 @@ const SnapshotExample: React.FC = () => {
287297
height,
288298
padding,
289299
quality,
300+
safetyFactor,
301+
safetyMargin,
290302
};
291303
console.log(params, 'params');
304+
// 在导出前更新快照扩展的样式控制参数
305+
const snapshotExt = lfRef.current?.extension?.snapshot;
306+
if (snapshotExt) {
307+
snapshotExt.useGlobalRules = useGlobalRules;
308+
snapshotExt.customCssRules = customCssRules;
309+
}
292310
await lfRef.current?.getSnapshot(fileName, params);
293311
};
294312

@@ -304,7 +322,15 @@ const SnapshotExample: React.FC = () => {
304322
height,
305323
padding,
306324
quality,
325+
safetyFactor,
326+
safetyMargin,
307327
};
328+
// 在预览前更新快照扩展的样式控制参数
329+
const snapshotExt = lfRef.current.extension?.snapshot;
330+
if (snapshotExt) {
331+
snapshotExt.useGlobalRules = useGlobalRules;
332+
snapshotExt.customCssRules = customCssRules;
333+
}
308334
lfRef.current
309335
.getSnapshotBlob(backgroundColor, fileType, params)
310336
.then(
@@ -336,7 +362,15 @@ const SnapshotExample: React.FC = () => {
336362
height,
337363
padding,
338364
quality,
365+
safetyFactor,
366+
safetyMargin,
339367
};
368+
// 在预览前更新快照扩展的样式控制参数
369+
const snapshotExt = lfRef.current.extension?.snapshot;
370+
if (snapshotExt) {
371+
snapshotExt.useGlobalRules = useGlobalRules;
372+
snapshotExt.customCssRules = customCssRules;
373+
}
340374
lfRef.current
341375
.getSnapshotBase64(backgroundColor, fileType, params)
342376
.then(
@@ -428,6 +462,16 @@ const SnapshotExample: React.FC = () => {
428462
value={height}
429463
onChange={(value) => handleInputChange(value, 'height')}
430464
/>
465+
<InputNumber
466+
addonBefore="安全系数:"
467+
value={safetyFactor}
468+
onChange={(value) => setSafetyFactor(value ?? 1.1)}
469+
/>
470+
<InputNumber
471+
addonBefore="安全边距:"
472+
value={safetyMargin}
473+
onChange={(value) => setSafetyMargin(value ?? 40)}
474+
/>
431475
</Space>
432476
<p></p>
433477
<Space>
@@ -447,7 +491,21 @@ const SnapshotExample: React.FC = () => {
447491
onChange={(value) => handleInputChange(value, 'quality')}
448492
/>
449493
<span>导出局部渲染:</span>
450-
<Switch defaultChecked onChange={(partial) => setPartial(partial)} />
494+
<Switch checked={partial} onChange={(checked) => setPartial(checked)} />
495+
<span>注入全局样式:</span>
496+
<Switch
497+
checked={useGlobalRules}
498+
onChange={(checked) => setUseGlobalRules(checked)}
499+
/>
500+
</Space>
501+
<p></p>
502+
<Space style={{ width: '100%' }} direction="vertical">
503+
<Input.TextArea
504+
rows={4}
505+
placeholder="自定义导出样式,支持标准CSS语法"
506+
value={customCssRules}
507+
onChange={(e) => setCustomCssRules(e.target.value)}
508+
/>
451509
</Space>
452510
<Divider />
453511
<Space>

0 commit comments

Comments
 (0)