Skip to content

Commit 2bfb3f9

Browse files
Mirrors406DymoneLewis
authored andcommitted
fix: snapshot插件导出图片受canvas的大小限制导致导出为空(计算规则、等比缩放)
1 parent d899d94 commit 2bfb3f9

3 files changed

Lines changed: 33 additions & 59 deletions

File tree

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

Lines changed: 31 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -315,31 +315,34 @@ export class Snapshot {
315315
}
316316

317317
/**
318-
* 根据浏览器类型获取Canvas尺寸限制
319-
* @returns 包含最大主维度和次维度的对象
318+
* 根据浏览器对 canvas 的大小限制,计算等比缩放比例
319+
* - 参考 MDN 最大的画布尺寸:
320+
* https://developer.mozilla.org/zh-CN/docs/Web/HTML/Reference/Elements/canvas#最大的画布尺寸
321+
*
322+
* @param width
323+
* @param height
320324
*/
321-
private getCanvasDimensionsByBrowser(): {
322-
maxCanvasDimension: number
323-
otherMaxCanvasDimension: number
324-
} {
325-
const userAgent = navigator.userAgent
325+
private getCanvasScaleRatio(width: number, height: number): number {
326+
/* 单边最大像素 */
327+
const maxCanvasSide = 32767
328+
/* 最大像素面积 */
329+
const maxCanvasArea = 268435456
326330

327-
// 默认值
328-
let maxCanvasDimension = 65535
329-
let otherMaxCanvasDimension = 4096
331+
const area = width * height
330332

331333
if (
332-
userAgent.indexOf('Chrome') !== -1 ||
333-
userAgent.indexOf('Edge') !== -1
334+
width <= maxCanvasSide &&
335+
height <= maxCanvasSide &&
336+
area <= maxCanvasArea
334337
) {
335-
maxCanvasDimension = 65535
336-
otherMaxCanvasDimension = 4096
337-
} else if (userAgent.indexOf('Firefox') !== -1) {
338-
maxCanvasDimension = 32767
339-
otherMaxCanvasDimension = 3814
338+
return 1
340339
}
341340

342-
return { maxCanvasDimension, otherMaxCanvasDimension }
341+
const widthScale = maxCanvasSide / width
342+
const heightScale = maxCanvasSide / height
343+
const areaScale = maxCanvasArea / area
344+
345+
return Math.min(widthScale, heightScale, areaScale)
343346
}
344347

345348
/**
@@ -406,62 +409,33 @@ export class Snapshot {
406409
// 为宽画布添加额外的安全边距,确保不会裁剪
407410
const safetyMargin = toImageOptions.safetyMargin || 0 // 额外的安全边距
408411

409-
// 获取当前浏览器类型,不同浏览器对canvas的限制不同
410-
const { maxCanvasDimension, otherMaxCanvasDimension } =
411-
this.getCanvasDimensionsByBrowser()
412-
const MAX_CANVAS_DIMENSION = maxCanvasDimension
413-
const OTHER_MAX_CANVAS_DIMENSION = otherMaxCanvasDimension
414-
415412
let targetWidth = bboxWidth * dpr
416413
let targetHeight = bboxHeight * dpr
417-
let scaleWidth = 1 //宽 缩放
418-
let scaleHeight = 1 //高 缩放
419-
// 对宽和高分别进行缩放,如chrome,矩形单边最大宽度不超过65535,如宽超过65535,那么高不能超过4096,否则像素会超,也会显示不出。
420-
if (
421-
targetWidth > MAX_CANVAS_DIMENSION &&
422-
targetHeight > OTHER_MAX_CANVAS_DIMENSION
423-
) {
424-
scaleWidth = MAX_CANVAS_DIMENSION / targetWidth
425-
scaleHeight = OTHER_MAX_CANVAS_DIMENSION / targetHeight
426-
} else if (
427-
targetWidth > OTHER_MAX_CANVAS_DIMENSION &&
428-
targetHeight > MAX_CANVAS_DIMENSION
429-
) {
430-
scaleWidth = OTHER_MAX_CANVAS_DIMENSION / targetWidth
431-
scaleHeight = MAX_CANVAS_DIMENSION / targetHeight
432-
} else if (
433-
targetWidth > MAX_CANVAS_DIMENSION &&
434-
targetHeight < OTHER_MAX_CANVAS_DIMENSION
435-
) {
436-
scaleHeight = scaleWidth = MAX_CANVAS_DIMENSION / targetWidth
437-
} else if (
438-
targetWidth < OTHER_MAX_CANVAS_DIMENSION &&
439-
targetHeight > MAX_CANVAS_DIMENSION
440-
) {
441-
scaleHeight = scaleWidth = MAX_CANVAS_DIMENSION / targetHeight
442-
}
443414

444-
if (scaleWidth < 1 || scaleHeight < 1) {
445-
targetWidth = Math.floor(targetWidth * scaleWidth)
446-
targetHeight = Math.floor(targetHeight * scaleHeight)
415+
// 超出 canvas 大小限制时,进行等比缩放
416+
const scaleRatio = this.getCanvasScaleRatio(targetWidth, targetHeight)
417+
if (scaleRatio < 1) {
418+
targetWidth = Math.floor(targetWidth * scaleRatio)
419+
targetHeight = Math.floor(targetHeight * scaleRatio)
447420
}
421+
448422
// 将导出区域移动到左上角,canvas 绘制的时候是从左上角开始绘制的
449423
// 在transform矩阵中加入padding值,确保左侧元素不会被截断
450424
// 对这个矩阵进行缩放,否则会导致截断
451425
;(copy.lastChild as SVGElement).style.transform =
452-
`matrix(${scaleWidth}, 0, 0, ${scaleHeight}, ${
453-
(-offsetX + TRANSLATE_X) * (1 / SCALE_X) * scaleWidth +
426+
`matrix(${scaleRatio}, 0, 0, ${scaleRatio}, ${
427+
(-offsetX + TRANSLATE_X) * (1 / SCALE_X) * scaleRatio +
454428
padding +
455429
factorWidth / 2 +
456430
safetyMargin
457-
}, ${(-offsetY + TRANSLATE_Y) * (1 / SCALE_Y) * scaleHeight + padding + factorHeight / 2 + safetyMargin})`
431+
}, ${(-offsetY + TRANSLATE_Y) * (1 / SCALE_Y) * scaleRatio + padding + factorHeight / 2 + safetyMargin})`
458432
canvas.width = targetWidth + (padding + safetyMargin) * 2 * dpr
459433
canvas.height = targetHeight + (padding + safetyMargin) * 2 * dpr
460434
const ctx = canvas.getContext('2d')
461435
if (ctx) {
462436
// 清空canvas
463437
ctx.clearRect(0, 0, canvas.width, canvas.height)
464-
ctx.scale(dpr * scaleWidth, dpr * scaleHeight)
438+
ctx.scale(dpr, dpr)
465439
// 如果有背景色,设置流程图导出的背景色
466440
if (backgroundColor) {
467441
ctx.fillStyle = backgroundColor

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ The export method supports the `toImageOptions` parameter with the following con
7070
- During export, the canvas will automatically handle wide canvas situations, adding safety factors and extra margins
7171
- During export, silent mode will be automatically enabled, disabling canvas interaction
7272
- Automatically converts relative path images in SVG to Base64 encoding <Badge type="warning">2.0.14 New</Badge>
73-
- 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
73+
- When the image exceeds the browser's canvas limit, it will automatically scale the image size proportionally to ensure successful export, but it will affect image clarity
7474
- You can fine-tune wide canvas behavior via `safetyFactor` and `safetyMargin` to avoid element cropping
7575
- 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
7676
- Anchors and rotate controls are automatically removed during export to prevent auxiliary elements from appearing in the image

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ lf.getSnapshot('流程图');
6969
- 导出时会自动处理宽画布情况,添加安全系数和额外边距
7070
- 导出过程中会自动开启静默模式,禁用画布交互
7171
- 自动将SVG中的相对路径图片转换为Base64编码<Badge type="warning">2.0.14新增</Badge>
72-
- 导出图片超过浏览器对canvas限制时,会自动缩放图片尺寸,确保导出成功,但会影响图片清晰度
72+
- 导出图片超过浏览器对canvas限制时,会自动等比缩放图片尺寸,确保导出成功,但会影响图片清晰度
7373
- 可通过 `safetyFactor``safetyMargin` 精细调整宽画布的安全余量,避免元素裁剪
7474
- `partial` 未显式传入时,默认沿用当前画布的局部渲染状态;导出期间如需切换渲染模式会临时切换并在导出完成后还原
7575
- 导出时会自动移除锚点与旋转控件,避免辅助元素进入图片

0 commit comments

Comments
 (0)