每个浏览器都限制了 canvas 画布大小,超过之后画布会一片空白,或者标签页的内存会耗尽。各浏览器的上限不同:桌面版 Chrome 的 canvas最大宽度是 32,767 像素,而 iOS Safari 总共只允许大约 6,700 万像素,也就是 8,192 见方。在笔记本上能正常导出的设计,到了 iPhone 上可能会失败,而且常常没有任何错误提示。
试一试
正在加载在线示例,代码在下方。
"use client";
import { isDocumentEngineError } from "fabricjs-document-engine";
import { useDocumentEngine } from "fabricjs-document-engine/react";
import { ExpandIcon, ImageIcon, MaximizeIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useEffect, useState } from "react";
import {
DemoCanvas,
DemoFrame,
seedShapes,
Status,
Toolbar,
ToolButton,
} from "./demo-ui";
import { useFabricCanvas } from "./use-fabric-canvas";
export default function SizeLimitsDemo() {
const t = useTranslations("demos");
const { elementRef, canvas } = useFabricCanvas();
const engine = useDocumentEngine(canvas);
const [message, setMessage] = useState<{
tone: "ok" | "error";
text: string;
} | null>(null);
useEffect(() => {
if (!engine) return;
engine.canvas.add(...seedShapes());
engine.clearHistory();
}, [engine]);
async function check(scale: number) {
if (!engine) return;
const preflight = await engine.preflightExport({ format: "png", scale });
const problem = preflight.problems[0];
setMessage(
problem
? {
tone: "error",
text: t("sizeLimits.refused", { code: problem.code, scale }),
}
: {
tone: "ok",
text: t("sizeLimits.fine", {
scale,
width: 480 * scale,
height: 300 * scale,
}),
}
);
}
function hugePage() {
if (!engine) return;
try {
engine.setPage({ width: 20000, height: 20000 });
} catch (error) {
setMessage({
tone: "error",
text: t("sizeLimits.page", {
code: isDocumentEngineError(error) ? error.code : String(error),
}),
});
}
}
return (
<DemoFrame>
<DemoCanvas elementRef={elementRef} label={t("canvas")} />
<Toolbar>
<ToolButton
icon={ImageIcon}
label={t("sizeLimits.small")}
disabled={!engine}
onClick={() => void check(4)}
/>
<ToolButton
icon={MaximizeIcon}
label={t("sizeLimits.large")}
primary
disabled={!engine}
onClick={() => void check(60)}
/>
<ToolButton
icon={ExpandIcon}
label={t("sizeLimits.hugePage")}
disabled={!engine}
onClick={hugePage}
/>
</Toolbar>
<Status tone={message?.tone ?? "idle"}>
{message?.text ?? t("sizeLimits.hint")}
</Status>
</DemoFrame>
);
}按 4× 导出没有问题。按 60× 导出会有 28,800 像素宽,在创建任何画布之前就被拒绝;20,000 × 20,000 的页面也一样。
限制
const engine = createDocumentEngine({
canvas,
limits: {
maxCanvasSide: 16384,
maxCanvasPixels: 8192 * 8192,
maxImagePixels: 8192 * 8192,
maxDocumentLength: 100_000_000,
},
});| 限制 | 默认值 | 检查范围 |
|---|---|---|
maxCanvasSide | 16,384 | 页面,以及 PNG、JPEG、WebP 和 PDF 导出 |
maxCanvasPixels | 67,108,864(8,192²) | 页面和位图导出 |
maxImagePixels | 67,108,864 | 每张图片解码后的像素 |
maxDocumentLength | 100,000,000 | 加载文档的 JSON |
默认值对应最严格的常见浏览器 iOS Safari。只有编辑器只在桌面浏览器上运行时,才需要调高。每项检查都是在创建画布之前做的一次乘法,几乎没有开销。
导出
const preflight = await engine.preflightExport({ format: "png", scale: 4 });
if (!preflight.ok) {
const tooLarge = preflight.problems.find(
(problem) => problem.code === "TOO_LARGE"
);
if (tooLarge) showMessage(tooLarge.message);
}超过 canvas 画布大小限制的导出会以 EXPORT_BLOCKED 失败,并附带一个 TOO_LARGE 问题,说明它本来会有多大。PDF 导出则会降低页面分辨率,所以仍然能得到 PDF。
调整画布大小
用 setPage 调整画布大小或修改背景。这次修改算一步撤销;超过限制的尺寸会在任何改动之前以 UNSAFE_DOCUMENT 被拒绝:
try {
engine.setPage({ width: 3000, height: 2000, background: "#ffffff" });
} catch (error) {
if (isDocumentEngineError(error)) showMessage(error.message);
}图片和文档
超过 maxImagePixels 的图片会以 TOO_LARGE 的原因加载失败;页面或 JSON 超过限制的文档会以 UNSAFE_DOCUMENT 被拒绝。两者都在 Fabric 解码或创建任何东西之前检查,不会占用多余内存。错误代码列出了所有代码,其他限制见安全。保持在 canvas 画布大小上限之内,任何设备都能打开和导出文档。