@logicflow/extension 2.0.17 → 2.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +6 -0
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/es/components/highlight/index.js +19 -3
- package/es/dynamic-group/index.js +0 -2
- package/es/style/raw.d.ts +1 -1
- package/es/style/raw.js +1 -1
- package/es/tools/proximity-connect/index.js +25 -4
- package/es/tools/snapshot/index.d.ts +47 -20
- package/es/tools/snapshot/index.js +459 -246
- package/lib/components/highlight/index.js +18 -2
- package/lib/dynamic-group/index.js +0 -2
- package/lib/style/raw.d.ts +1 -1
- package/lib/style/raw.js +1 -1
- package/lib/tools/proximity-connect/index.js +24 -3
- package/lib/tools/snapshot/index.d.ts +47 -20
- package/lib/tools/snapshot/index.js +459 -246
- package/package.json +1 -1
- package/src/components/highlight/index.ts +30 -5
- package/src/dynamic-group/index.ts +0 -3
- package/src/style/raw.ts +4 -0
- package/src/tools/proximity-connect/index.ts +30 -3
- package/src/tools/snapshot/index.ts +325 -152
- package/stats.html +1 -1
|
@@ -70,14 +70,18 @@ export class Snapshot {
|
|
|
70
70
|
) => await this.getSnapshot(fileName, toImageOptions)
|
|
71
71
|
|
|
72
72
|
/* 获取Blob对象 */
|
|
73
|
-
lf.getSnapshotBlob = async (
|
|
74
|
-
|
|
73
|
+
lf.getSnapshotBlob = async (
|
|
74
|
+
backgroundColor?: string, // 兼容老的使用方式
|
|
75
|
+
fileType?: string,
|
|
76
|
+
toImageOptions?: ToImageOptions,
|
|
77
|
+
) => await this.getSnapshotBlob(backgroundColor, fileType, toImageOptions)
|
|
75
78
|
|
|
76
79
|
/* 获取Base64对象 */
|
|
77
80
|
lf.getSnapshotBase64 = async (
|
|
78
|
-
backgroundColor?: string,
|
|
81
|
+
backgroundColor?: string, // 兼容老的使用方式
|
|
79
82
|
fileType?: string,
|
|
80
|
-
|
|
83
|
+
toImageOptions?: ToImageOptions,
|
|
84
|
+
) => await this.getSnapshotBase64(backgroundColor, fileType, toImageOptions)
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/**
|
|
@@ -144,123 +148,134 @@ export class Snapshot {
|
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
/**
|
|
147
|
-
*
|
|
148
|
-
* @param
|
|
149
|
-
* @
|
|
151
|
+
* 将图片转换为base64格式
|
|
152
|
+
* @param url - 图片URL
|
|
153
|
+
* @returns Promise<string> - base64字符串
|
|
150
154
|
*/
|
|
151
|
-
async
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
155
|
+
private async convertImageToBase64(url: string): Promise<string> {
|
|
156
|
+
return new Promise((resolve, reject) => {
|
|
157
|
+
const img = new Image()
|
|
158
|
+
img.crossOrigin = 'anonymous' // 处理跨域问题
|
|
159
|
+
img.onload = () => {
|
|
160
|
+
const canvas = document.createElement('canvas')
|
|
161
|
+
canvas.width = img.width
|
|
162
|
+
canvas.height = img.height
|
|
163
|
+
const ctx = canvas.getContext('2d')
|
|
164
|
+
ctx?.drawImage(img, 0, 0)
|
|
165
|
+
const base64 = canvas.toDataURL('image/png')
|
|
166
|
+
resolve(base64)
|
|
167
|
+
}
|
|
168
|
+
img.onerror = () => {
|
|
169
|
+
reject(new Error(`Failed to load image: ${url}`))
|
|
170
|
+
}
|
|
171
|
+
img.src = url
|
|
161
172
|
})
|
|
162
|
-
// 画布当前渲染模式和用户导出渲染模式不一致时,需要更新画布
|
|
163
|
-
if (curPartial !== partial) {
|
|
164
|
-
this.lf.graphModel.setPartial(partial)
|
|
165
|
-
this.lf.graphModel.eventCenter.once('graph:updated', async () => {
|
|
166
|
-
await this.snapshot(fileName, toImageOptions)
|
|
167
|
-
// 恢复原来渲染模式
|
|
168
|
-
this.lf.graphModel.setPartial(curPartial)
|
|
169
|
-
})
|
|
170
|
-
} else {
|
|
171
|
-
await this.snapshot(fileName, toImageOptions)
|
|
172
|
-
}
|
|
173
|
-
// 恢复原来配置
|
|
174
|
-
this.lf.updateEditConfig(editConfig)
|
|
175
173
|
}
|
|
176
174
|
|
|
177
175
|
/**
|
|
178
|
-
*
|
|
179
|
-
* @param
|
|
180
|
-
* @
|
|
176
|
+
* 检查URL是否为相对路径
|
|
177
|
+
* @param url - 要检查的URL
|
|
178
|
+
* @returns boolean - 是否为相对路径
|
|
181
179
|
*/
|
|
182
|
-
private
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const svgString = new XMLSerializer().serializeToString(copy)
|
|
190
|
-
const blob = new Blob([svgString], {
|
|
191
|
-
type: 'image/svg+xml;charset=utf-8',
|
|
192
|
-
})
|
|
193
|
-
const url = URL.createObjectURL(blob)
|
|
194
|
-
this.triggerDownload(url)
|
|
195
|
-
} else {
|
|
196
|
-
this.getCanvasData(svg, toImageOptions ?? {}).then(
|
|
197
|
-
(canvas: HTMLCanvasElement) => {
|
|
198
|
-
// canvas元素 => base64 url image/octet-stream: 确保所有浏览器都能正常下载
|
|
199
|
-
const imgUrl = canvas
|
|
200
|
-
.toDataURL(`image/${fileType}`, quality)
|
|
201
|
-
.replace(`image/${fileType}`, 'image/octet-stream')
|
|
202
|
-
this.triggerDownload(imgUrl)
|
|
203
|
-
},
|
|
204
|
-
)
|
|
205
|
-
}
|
|
180
|
+
private isRelativePath(url: string): boolean {
|
|
181
|
+
return (
|
|
182
|
+
!url.startsWith('data:') &&
|
|
183
|
+
!url.startsWith('http://') &&
|
|
184
|
+
!url.startsWith('https://') &&
|
|
185
|
+
!url.startsWith('//')
|
|
186
|
+
)
|
|
206
187
|
}
|
|
207
188
|
|
|
208
189
|
/**
|
|
209
|
-
*
|
|
210
|
-
* @param
|
|
211
|
-
* @param fileType
|
|
212
|
-
* @returns
|
|
190
|
+
* 处理SVG中的图片元素
|
|
191
|
+
* @param element - SVG元素
|
|
213
192
|
*/
|
|
214
|
-
async
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
193
|
+
private async processImages(element: Element): Promise<void> {
|
|
194
|
+
// 处理image元素
|
|
195
|
+
const images = element.getElementsByTagName('image')
|
|
196
|
+
for (let i = 0; i < images.length; i++) {
|
|
197
|
+
const image = images[i]
|
|
198
|
+
const href =
|
|
199
|
+
image.getAttributeNS('http://www.w3.org/1999/xlink', 'href') ||
|
|
200
|
+
image.getAttribute('href')
|
|
201
|
+
if (href && this.isRelativePath(href)) {
|
|
202
|
+
try {
|
|
203
|
+
const base64 = await this.convertImageToBase64(href)
|
|
204
|
+
image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', base64)
|
|
205
|
+
image.setAttribute('href', base64)
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.warn(`Failed to convert image to base64: ${href}`, error)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 处理foreignObject中的img元素
|
|
213
|
+
const foreignObjects = element.getElementsByTagName('foreignObject')
|
|
214
|
+
for (let i = 0; i < foreignObjects.length; i++) {
|
|
215
|
+
const foreignObject = foreignObjects[i]
|
|
216
|
+
const images = foreignObject.getElementsByTagName('img')
|
|
217
|
+
for (let j = 0; j < images.length; j++) {
|
|
218
|
+
const image = images[j]
|
|
219
|
+
const src = image.getAttribute('src')
|
|
220
|
+
if (src && this.isRelativePath(src)) {
|
|
221
|
+
try {
|
|
222
|
+
const base64 = await this.convertImageToBase64(src)
|
|
223
|
+
image.setAttribute('src', base64)
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.warn(`Failed to convert image to base64: ${src}`, error)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
233
230
|
}
|
|
234
231
|
|
|
235
232
|
/**
|
|
236
|
-
*
|
|
237
|
-
* @param
|
|
238
|
-
* @param fileType
|
|
233
|
+
* 克隆并处理画布节点
|
|
234
|
+
* @param svg
|
|
239
235
|
* @returns
|
|
240
236
|
*/
|
|
241
|
-
async
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
): Promise<
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
237
|
+
private async cloneSvg(
|
|
238
|
+
svg: Element,
|
|
239
|
+
addStyle: boolean = true,
|
|
240
|
+
): Promise<Node> {
|
|
241
|
+
const copy = svg.cloneNode(true) as Element
|
|
242
|
+
const graph = copy.lastChild as Element
|
|
243
|
+
let childLength = graph?.childNodes?.length
|
|
244
|
+
if (childLength) {
|
|
245
|
+
for (let i = 0; i < childLength; i++) {
|
|
246
|
+
const lfLayer = graph?.childNodes[i] as SVGGraphicsElement
|
|
247
|
+
// 只保留包含节点和边的基础图层进行下载,其他图层删除
|
|
248
|
+
const layerClassList =
|
|
249
|
+
lfLayer.classList && Array.from(lfLayer.classList)
|
|
250
|
+
if (layerClassList && layerClassList.indexOf('lf-base') < 0) {
|
|
251
|
+
graph?.removeChild(graph.childNodes[i])
|
|
252
|
+
childLength--
|
|
253
|
+
i--
|
|
254
|
+
} else {
|
|
255
|
+
// 删除锚点
|
|
256
|
+
const lfBase = graph?.childNodes[i]
|
|
257
|
+
lfBase &&
|
|
258
|
+
lfBase.childNodes.forEach((item) => {
|
|
259
|
+
const element = item as SVGGraphicsElement
|
|
260
|
+
this.removeAnchor(element.firstChild!)
|
|
261
|
+
this.removeRotateControl(element.firstChild!)
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 处理图片路径
|
|
268
|
+
await this.processImages(copy)
|
|
269
|
+
|
|
270
|
+
// 设置css样式
|
|
271
|
+
if (addStyle) {
|
|
272
|
+
const style = document.createElement('style')
|
|
273
|
+
style.innerHTML = this.getClassRules()
|
|
274
|
+
const foreignObject = document.createElement('foreignObject')
|
|
275
|
+
foreignObject.appendChild(style)
|
|
276
|
+
copy.appendChild(foreignObject)
|
|
277
|
+
}
|
|
278
|
+
return copy
|
|
264
279
|
}
|
|
265
280
|
|
|
266
281
|
/**
|
|
@@ -302,7 +317,7 @@ export class Snapshot {
|
|
|
302
317
|
toImageOptions: ToImageOptions,
|
|
303
318
|
): Promise<HTMLCanvasElement> {
|
|
304
319
|
const { width, height, backgroundColor, padding = 40 } = toImageOptions
|
|
305
|
-
const copy = this.cloneSvg(svg, false)
|
|
320
|
+
const copy = await this.cloneSvg(svg, false)
|
|
306
321
|
|
|
307
322
|
let dpr = window.devicePixelRatio || 1
|
|
308
323
|
if (dpr < 1) {
|
|
@@ -335,21 +350,31 @@ export class Snapshot {
|
|
|
335
350
|
const { transformModel } = graphModel
|
|
336
351
|
const { SCALE_X, SCALE_Y, TRANSLATE_X, TRANSLATE_Y } = transformModel
|
|
337
352
|
|
|
353
|
+
// 计算实际宽高,考虑缩放因素
|
|
354
|
+
// 在宽画布情况下,getBoundingClientRect可能无法获取到所有元素的边界
|
|
355
|
+
// 因此我们添加一个安全系数来确保能够容纳所有元素
|
|
356
|
+
const safetyFactor = 1.1 // 安全系数,增加20%的空间
|
|
357
|
+
const actualWidth = (bbox.width / SCALE_X) * safetyFactor
|
|
358
|
+
const actualHeight = (bbox.height / SCALE_Y) * safetyFactor
|
|
359
|
+
|
|
338
360
|
// 将导出区域移动到左上角,canvas 绘制的时候是从左上角开始绘制的
|
|
361
|
+
// 在transform矩阵中加入padding值,确保左侧元素不会被截断
|
|
339
362
|
;(copy.lastChild as SVGElement).style.transform = `matrix(1, 0, 0, 1, ${
|
|
340
|
-
(-offsetX + TRANSLATE_X) * (1 / SCALE_X)
|
|
341
|
-
}, ${(-offsetY + TRANSLATE_Y) * (1 / SCALE_Y)})`
|
|
363
|
+
(-offsetX + TRANSLATE_X) * (1 / SCALE_X) + padding / dpr
|
|
364
|
+
}, ${(-offsetY + TRANSLATE_Y) * (1 / SCALE_Y) + padding / dpr})`
|
|
342
365
|
|
|
343
|
-
//
|
|
344
|
-
const bboxWidth = Math.ceil(
|
|
345
|
-
const bboxHeight = Math.ceil(
|
|
366
|
+
// 包含所有元素的最小宽高,确保足够大以容纳所有元素
|
|
367
|
+
const bboxWidth = Math.ceil(actualWidth)
|
|
368
|
+
const bboxHeight = Math.ceil(actualHeight)
|
|
346
369
|
const canvas = document.createElement('canvas')
|
|
347
370
|
canvas.style.width = `${bboxWidth}px`
|
|
348
371
|
canvas.style.height = `${bboxHeight}px`
|
|
349
372
|
|
|
350
373
|
// 宽高值 默认加padding 40,保证图形不会紧贴着下载图片
|
|
351
|
-
|
|
352
|
-
|
|
374
|
+
// 为宽画布添加额外的安全边距,确保不会裁剪
|
|
375
|
+
const safetyMargin = 40 // 额外的安全边距
|
|
376
|
+
canvas.width = bboxWidth * dpr + padding * 2 + safetyMargin
|
|
377
|
+
canvas.height = bboxHeight * dpr + padding * 2 + safetyMargin
|
|
353
378
|
const ctx = canvas.getContext('2d')
|
|
354
379
|
if (ctx) {
|
|
355
380
|
// 清空canvas
|
|
@@ -387,19 +412,22 @@ export class Snapshot {
|
|
|
387
412
|
? copyCanvas(canvas, width, height).height
|
|
388
413
|
: canvas.height,
|
|
389
414
|
}).then((imageBitmap) => {
|
|
390
|
-
|
|
415
|
+
// 由于在transform矩阵中已经考虑了padding,这里不再需要额外的padding偏移
|
|
416
|
+
ctx?.drawImage(imageBitmap, 0, 0)
|
|
391
417
|
resolve(
|
|
392
418
|
width && height ? copyCanvas(canvas, width, height) : canvas,
|
|
393
419
|
)
|
|
394
420
|
})
|
|
395
421
|
} else {
|
|
396
|
-
|
|
422
|
+
// 由于在transform矩阵中已经考虑了padding,这里不再需要额外的padding偏移
|
|
423
|
+
ctx?.drawImage(img, 0, 0)
|
|
397
424
|
resolve(
|
|
398
425
|
width && height ? copyCanvas(canvas, width, height) : canvas,
|
|
399
426
|
)
|
|
400
427
|
}
|
|
401
428
|
} catch (e) {
|
|
402
|
-
|
|
429
|
+
// 由于在transform矩阵中已经考虑了padding,这里不再需要额外的padding偏移
|
|
430
|
+
ctx?.drawImage(img, 0, 0)
|
|
403
431
|
resolve(width && height ? copyCanvas(canvas, width, height) : canvas)
|
|
404
432
|
}
|
|
405
433
|
}
|
|
@@ -421,45 +449,190 @@ export class Snapshot {
|
|
|
421
449
|
}
|
|
422
450
|
|
|
423
451
|
/**
|
|
424
|
-
*
|
|
425
|
-
* @param
|
|
426
|
-
* @
|
|
452
|
+
* 封装导出前的通用处理逻辑:局部渲染模式处理、静默模式处理
|
|
453
|
+
* @param callback 实际执行的导出操作回调函数
|
|
454
|
+
* @param toImageOptions 导出图片选项
|
|
455
|
+
* @returns 返回回调函数的执行结果
|
|
427
456
|
*/
|
|
428
|
-
private
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
457
|
+
private async withExportPreparation<T>(
|
|
458
|
+
callback: () => Promise<T>,
|
|
459
|
+
toImageOptions?: ToImageOptions,
|
|
460
|
+
): Promise<T> {
|
|
461
|
+
// 获取当前局部渲染状态
|
|
462
|
+
const curPartial = this.lf.graphModel.getPartial()
|
|
463
|
+
const { partial = curPartial } = toImageOptions ?? {}
|
|
464
|
+
// 获取流程图配置
|
|
465
|
+
const editConfig = this.lf.getEditConfig()
|
|
466
|
+
|
|
467
|
+
// 开启静默模式:如果元素多的话 避免用户交互 感知卡顿
|
|
468
|
+
this.lf.updateEditConfig({
|
|
469
|
+
isSilentMode: true,
|
|
470
|
+
stopScrollGraph: true,
|
|
471
|
+
stopMoveGraph: true,
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
let result: T
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
// 如果画布的渲染模式与导出渲染模式不一致,则切换渲染模式
|
|
478
|
+
if (curPartial !== partial) {
|
|
479
|
+
this.lf.graphModel.setPartial(partial)
|
|
480
|
+
// 等待画布更新完成
|
|
481
|
+
result = await new Promise<T>((resolve) => {
|
|
482
|
+
this.lf.graphModel.eventCenter.once('graph:updated', async () => {
|
|
483
|
+
const callbackResult = await callback()
|
|
484
|
+
// 恢复原来渲染模式
|
|
485
|
+
this.lf.graphModel.setPartial(curPartial)
|
|
486
|
+
resolve(callbackResult)
|
|
487
|
+
})
|
|
488
|
+
})
|
|
489
|
+
} else {
|
|
490
|
+
// 直接执行回调
|
|
491
|
+
result = await callback()
|
|
452
492
|
}
|
|
493
|
+
} finally {
|
|
494
|
+
// 恢复原来配置
|
|
495
|
+
this.lf.updateEditConfig(editConfig)
|
|
453
496
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
497
|
+
|
|
498
|
+
return result
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* 导出画布:导出前的处理画布工作,局部渲染模式处理、静默模式处理
|
|
503
|
+
* @param fileName
|
|
504
|
+
* @param toImageOptions
|
|
505
|
+
*/
|
|
506
|
+
async getSnapshot(fileName?: string, toImageOptions?: ToImageOptions) {
|
|
507
|
+
await this.withExportPreparation(
|
|
508
|
+
() => this.snapshot(fileName, toImageOptions),
|
|
509
|
+
toImageOptions,
|
|
510
|
+
)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* 下载图片
|
|
515
|
+
* @param fileName
|
|
516
|
+
* @param toImageOptions
|
|
517
|
+
*/
|
|
518
|
+
private async snapshot(fileName?: string, toImageOptions?: ToImageOptions) {
|
|
519
|
+
const { fileType = 'png', quality } = toImageOptions ?? {}
|
|
520
|
+
this.fileName = `${fileName ?? `logic-flow.${Date.now()}`}.${fileType}`
|
|
521
|
+
const svg = this.getSvgRootElement(this.lf)
|
|
522
|
+
await updateImageSource(svg as SVGElement)
|
|
523
|
+
if (fileType === 'svg') {
|
|
524
|
+
const copy = await this.cloneSvg(svg)
|
|
525
|
+
const svgString = new XMLSerializer().serializeToString(copy)
|
|
526
|
+
const blob = new Blob([svgString], {
|
|
527
|
+
type: 'image/svg+xml;charset=utf-8',
|
|
528
|
+
})
|
|
529
|
+
const url = URL.createObjectURL(blob)
|
|
530
|
+
this.triggerDownload(url)
|
|
531
|
+
} else {
|
|
532
|
+
this.getCanvasData(svg, toImageOptions ?? {}).then(
|
|
533
|
+
(canvas: HTMLCanvasElement) => {
|
|
534
|
+
// canvas元素 => base64 url image/octet-stream: 确保所有浏览器都能正常下载
|
|
535
|
+
const imgUrl = canvas
|
|
536
|
+
.toDataURL(`image/${fileType}`, quality)
|
|
537
|
+
.replace(`image/${fileType}`, 'image/octet-stream')
|
|
538
|
+
this.triggerDownload(imgUrl)
|
|
539
|
+
},
|
|
540
|
+
)
|
|
461
541
|
}
|
|
462
|
-
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* 获取Blob对象
|
|
546
|
+
* @param fileType
|
|
547
|
+
* @param toImageOptions
|
|
548
|
+
* @returns
|
|
549
|
+
*/
|
|
550
|
+
async getSnapshotBlob(
|
|
551
|
+
backgroundColor?: string,
|
|
552
|
+
fileType?: string,
|
|
553
|
+
toImageOptions?: ToImageOptions,
|
|
554
|
+
): Promise<SnapshotResponse> {
|
|
555
|
+
return await this.withExportPreparation(
|
|
556
|
+
() => this.snapshotBlob(toImageOptions, fileType, backgroundColor),
|
|
557
|
+
toImageOptions,
|
|
558
|
+
)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// 内部方法处理blob转换
|
|
562
|
+
private async snapshotBlob(
|
|
563
|
+
toImageOptions?: ToImageOptions,
|
|
564
|
+
baseFileType?: string,
|
|
565
|
+
backgroundColor?: string,
|
|
566
|
+
): Promise<SnapshotResponse> {
|
|
567
|
+
const { fileType = baseFileType } = toImageOptions ?? {}
|
|
568
|
+
const svg = this.getSvgRootElement(this.lf)
|
|
569
|
+
await updateImageSource(svg as SVGElement)
|
|
570
|
+
return new Promise((resolve) => {
|
|
571
|
+
this.getCanvasData(svg, {
|
|
572
|
+
backgroundColor,
|
|
573
|
+
...(toImageOptions ?? {}),
|
|
574
|
+
}).then((canvas: HTMLCanvasElement) => {
|
|
575
|
+
canvas.toBlob(
|
|
576
|
+
(blob) => {
|
|
577
|
+
// 输出图片数据以及图片宽高
|
|
578
|
+
resolve({
|
|
579
|
+
data: blob!,
|
|
580
|
+
width: canvas.width,
|
|
581
|
+
height: canvas.height,
|
|
582
|
+
})
|
|
583
|
+
},
|
|
584
|
+
`image/${fileType ?? 'png'}`,
|
|
585
|
+
)
|
|
586
|
+
})
|
|
587
|
+
})
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* 获取base64对象
|
|
592
|
+
* @param backgroundColor
|
|
593
|
+
* @param fileType
|
|
594
|
+
* @param toImageOptions
|
|
595
|
+
* @returns
|
|
596
|
+
*/
|
|
597
|
+
async getSnapshotBase64(
|
|
598
|
+
backgroundColor?: string,
|
|
599
|
+
fileType?: string,
|
|
600
|
+
toImageOptions?: ToImageOptions,
|
|
601
|
+
): Promise<SnapshotResponse> {
|
|
602
|
+
console.log(
|
|
603
|
+
'getSnapshotBase64---------------',
|
|
604
|
+
backgroundColor,
|
|
605
|
+
fileType,
|
|
606
|
+
toImageOptions,
|
|
607
|
+
)
|
|
608
|
+
return await this.withExportPreparation(
|
|
609
|
+
() => this._getSnapshotBase64(backgroundColor, fileType, toImageOptions),
|
|
610
|
+
toImageOptions,
|
|
611
|
+
)
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// 内部方法处理实际的base64转换
|
|
615
|
+
private async _getSnapshotBase64(
|
|
616
|
+
backgroundColor?: string,
|
|
617
|
+
baseFileType?: string,
|
|
618
|
+
toImageOptions?: ToImageOptions,
|
|
619
|
+
): Promise<SnapshotResponse> {
|
|
620
|
+
const { fileType = baseFileType } = toImageOptions ?? {}
|
|
621
|
+
const svg = this.getSvgRootElement(this.lf)
|
|
622
|
+
await updateImageSource(svg as SVGElement)
|
|
623
|
+
return new Promise((resolve) => {
|
|
624
|
+
this.getCanvasData(svg, {
|
|
625
|
+
backgroundColor,
|
|
626
|
+
...(toImageOptions ?? {}),
|
|
627
|
+
}).then((canvas: HTMLCanvasElement) => {
|
|
628
|
+
const base64 = canvas.toDataURL(`image/${fileType ?? 'png'}`)
|
|
629
|
+
resolve({
|
|
630
|
+
data: base64,
|
|
631
|
+
width: canvas.width,
|
|
632
|
+
height: canvas.height,
|
|
633
|
+
})
|
|
634
|
+
})
|
|
635
|
+
})
|
|
463
636
|
}
|
|
464
637
|
}
|
|
465
638
|
|