大尺寸 & 高分辨率导出
大幅面印刷需要很高的输出分辨率,往往超出浏览器 canvas 的能力。本文说明如何在 Ydesign(@ydesign/react-editor / @ydesign/core)里导出印刷级成品,适用于横幅、海报等大画幅场景。
快速上手
- 选择渲染方式:客户端(桌面端,单边 ≤ 8k px)或 服务端 / 矢量(单边 > 8k,或移动端)。
- 客户端路径:将
multiplier设为 2–4,并在目标设备上实测。 - 印刷自检:源图每一边须满足「物理毫米 × 目标 DPI ÷ 25.4」。
- 矢量输出选项:
- 客户端:导出 SVG / HTML(与分辨率无关,能力受功能支持影响)。
- 服务端:通过 云渲染 API 或规划中的
@ydesign/pdf-export导出矢量 PDF。
- 导出前再换成高清素材;编辑器内继续用预览图。
挑战在哪
大画幅设计经常要求浏览器无法直接渲染的分辨率。例如:
- 1.5 m × 3 m 喷绘 @ 200 DPI(1500 × 3000 mm)≈ 11,811 × 23,622 像素
- 3 m × 3 m 喷绘 @ 200 DPI(3000 × 3000 mm)≈ 23,622 × 23,622 像素
浏览器按浏览器 / 系统 / 硬件限制 canvas 尺寸,单边通常约 4,000–16,000 像素。Ydesign 不会额外限制导出尺寸;失败来自浏览器与硬件上限,不是 SDK 人为封顶。
浏览器渲染上限
影响因素包括:
- 浏览器:Chrome、Firefox、Safari 上限不同
- 操作系统:macOS / Windows / Linux 内存策略不同
- 硬件:GPU 显存与可用 RAM 决定实际可用上限
- 经验区间:单边约 4,000–16,000 像素
超出时可能出现:
- canvas 渲染失败
- 标签页或浏览器崩溃
- 导出图残缺、全透明或损坏
如何选型
按场景、受众与设计复杂度选择策略。
决策速查
- 客户端(桌面,单边 ≤ 8k):实时、快;
multiplier取 2–4。 - 服务端 / 矢量(> 8k、移动端或关键印刷):云渲染 API、
@ydesign/core/node(规划中)、或 SVG / HTML(在支持的场景)。 - 混合:按设备与设计复杂度探测,接近上限时回退到服务端 / 矢量。
客户端渲染
适合:
- 单边约 ≤ 8,000 像素
- 高性能桌面设备
- 较小印品(名片、传单、A 系列海报)
- 需要实时预览与即时导出
限制:
- 移动端或低端机易失败
- 超大导出占内存高
- 体积越大耗时越长
服务端渲染
适合:
- 单边超过约 8,000 像素
- 移动端或低端设备用户
- 需要跨设备结果一致
- 大批量自动化流水线
- 矢量 PDF(与像素上限无关)
可选方案:
- 云渲染 API — 托管服务,无需自建
@ydesign/core/node(规划中) — 自建 Node,数据不出内网
混合方案
按设备能力分流导出:
const isHighEndDevice =
(navigator.hardwareConcurrency ?? 0) >= 8 &&
// @ts-expect-error deviceMemory 非所有浏览器都有
(navigator.deviceMemory ?? 0) >= 8;
const exportDesign = async (store, multiplier = 2) => {
const maxSide = Math.max(store.width, store.height) * multiplier;
if (maxSide > 8000 || !isHighEndDevice) {
return await exportViaCloudAPI(store);
}
return await store.saveAsImage({ multiplier });
};
客户端高清导出
用 multiplier 放大导出像素,无需改编辑器画布尺寸。数值越大,输出越大、越清晰。
理解 multiplier
multiplier 会按倍数放大画布宽高后再出图:
// 画布:1200 × 1200 px
// multiplier: 2 → 输出 2400 × 2400 px
// multiplier: 4 → 输出 4800 × 4800 px
await store.saveAsImage({ multiplier: 2 });
注意:multiplier 提高的是渲染像素(清晰度);store 上的 dpi / unit 影响标尺与物理尺寸换算。详见 单位与度量。
实务上限
高清建议从 multiplier: 2 起;大幅面可试 4 或 8,务必在目标设备实测:
import { unitToPx } from '@ydesign/react-editor/utils/unit';
// 目标约 1.2 m × 1.2 m @ 200 DPI ≈ 9,449 × 9,449 px
// 编辑器画布保持可交互的小尺寸,例如 1,200 × 1,200
store.setSize({ width: 1200, height: 1200 });
// 导出时用约 8× 拉到接近目标像素
await store.saveAsImage({ multiplier: 8 });
警告:multiplier 很高(如 8+)时,部分设备会崩溃。关键流程请优先服务端渲染。
完整客户端示例
import { unitToPx } from '@ydesign/react-editor/utils/unit';
// 目标:约 1.2 m × 1.2 m(1200 × 1200 mm)@ 200 DPI
const widthMm = 1200;
const heightMm = 1200;
const targetDPI = 200;
const editorDPI = 72; // 编辑器常用屏幕基准
// 编辑器用 72 DPI 对应的像素,保持可交互
const editorWidth = unitToPx({ unitVal: widthMm, unit: 'mm', dpi: editorDPI });
const editorHeight = unitToPx({ unitVal: heightMm, unit: 'mm', dpi: editorDPI });
// ≈ 3402 × 3402 px
store.setSize({ width: editorWidth, height: editorHeight });
store.setUnit({ unit: 'mm', dpi: editorDPI });
// 导出清晰度:200 / 72 ≈ 2.78
const multiplier = targetDPI / editorDPI;
await store.saveAsImage({
multiplier,
format: 'png',
fileName: 'banner.png',
});
按比例编辑(标尺与 DPI)
编辑器用较小画布保流畅,标尺仍显示真实物理尺寸(毫米):
import { unitToPx } from '@ydesign/react-editor/utils/unit';
// 目标:3 m × 3 m(3000 × 3000 mm),按 1:10 缩小编辑
const scale = 0.1;
const targetMm = 3000;
const editorDPI = 72;
// 代理画布:300 mm × 300 mm → 约 850 × 850 px
const editorPx = unitToPx({
unitVal: targetMm * scale,
unit: 'mm',
dpi: editorDPI,
});
store.setSize({ width: editorPx, height: editorPx });
// 标尺读成 3000 mm(画布实际是 300 mm)
store.setUnit({
unit: 'mm',
dpi: editorDPI * scale, // 7.2;像素 × 25.4 / 7.2 ≈ 3000 mm
});
导出时再用真实目标 DPI(如 200)配合 multiplier,或走服务端 / 矢量。更多见 单位与度量。
栅格 PDF:页尺寸 vs 清晰度
客户端 PDF(见 PDF 导出)通常是栅格 PDF(每页是嵌入 PDF 的位图)。两个概念要分开:
- 页的物理尺寸:由画布像素与
dpi决定(UI 用mm显示时:( mm = px \times 25.4 / dpi )) - 渲染清晰度:由
multiplier决定:有效 DPI ≈dpi × multiplier(概念上)
图片导出示例:
await store.saveAsImage({
fileName: 'design.png',
multiplier: 2,
});
栅格 PDF 的完整参数、出血与裁切线见 PDF 导出。大幅面若需要很高 multiplier,优先服务端或矢量 PDF。
素材替换策略
编辑器用低清预览图保性能,导出前再换成高清原图。
把高清地址存在自定义字段
// 添加图片时同时记下预览图与高清图
element.set({
src: 'https://example.com/preview-800px.jpg',
// Fabric / Ydesign 自定义扩展字段,会随 toJSON 序列化
// (具体字段名可按业务约定,例如 keyValues / custom)
custom: {
highResSrc: 'https://example.com/original-5000px.jpg',
},
});
导出前替换
import type { StoreType } from '@ydesign/react-editor/model/store';
const swapHighResAssets = async (store: StoreType) => {
const canvas = store.editor?.canvas;
if (!canvas) return () => {};
const originals: { obj: any; src: string }[] = [];
for (const obj of canvas.getObjects()) {
const highRes = obj.custom?.highResSrc;
if (obj.type === 'image' && highRes) {
originals.push({ obj, src: obj.getSrc?.() ?? obj.src });
await obj.setSrc(highRes);
obj.setCoords();
}
}
canvas.requestRenderAll();
return () => {
originals.forEach(({ obj, src }) => {
void obj.setSrc(src);
});
canvas.requestRenderAll();
};
};
const restore = await swapHighResAssets(store);
try {
await store.saveAsImage({ multiplier: 4, format: 'png' });
} finally {
restore();
}
实际项目里建议把「替换 / 还原」封装成工具函数,并处理
setSrc异步加载完成后再导出。
校验图片分辨率
用 getImageSize 检查是否够用:
import { getImageSize } from '@ydesign/react-editor/utils/image';
const validateImageResolution = async (src: string, targetDPI: number, widthMm: number, heightMm: number) => {
const requiredWidth = (widthMm * targetDPI) / 25.4;
const requiredHeight = (heightMm * targetDPI) / 25.4;
const { width, height } = await getImageSize(src);
const ok = width >= requiredWidth && height >= requiredHeight;
if (!ok) {
console.warn(
`图片分辨率(${width}×${height})可能不足以支撑 ` +
`${widthMm}×${heightMm} mm @ ${targetDPI} DPI ` +
`(至少需要约 ${Math.ceil(requiredWidth)}×${Math.ceil(requiredHeight)} px)`
);
}
return ok;
};
矢量 PDF 导出
矢量 PDF 与分辨率无关,可绕过像素上限。可选托管云渲染或自建 Node 包。
云渲染 API
当前托管接口以栅格图为主(format: jpeg | png | webp + multiplier)。完整请求见 云渲染 API。
const json = store.toJSON();
const res = await fetch('https://api.ydesign.com/api/render/image', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
json,
format: 'png',
multiplier: 4,
fonts: [
/* 设计稿用到的自定义字体 URL */
],
}),
});
const { url } = await res.json();
矢量 PDF(format: 'pdf', vector: true)属于规划能力,到位后会与云渲染 / @ydesign/pdf-export 对齐;在此之前大幅面优先「高 multiplier 栅格云渲染」或自建 Node。
Node 包(@ydesign/pdf-export / @ydesign/core/node)
// 规划中的矢量 PDF API 形态
import { jsonToPDF } from '@ydesign/pdf-export';
const json = store.toJSON();
await jsonToPDF(json, './output.pdf');
自建无头渲染见 服务端图像生成;客户端栅格 PDF 见 PDF 导出。
SVG / HTML 等替代方案
需要分辨率无关的中间格式时,可导出 SVG / HTML(能力依赖元素类型,复杂滤镜未必 1:1)。要求像素级一致时,仍用 PNG / JPEG 或 PDF。总览见 导出与导入。
示例:3 m × 3 m 喷绘(3000 × 3000 mm @ 200 DPI)
推荐(服务端):
- 用
store.toJSON()提交 云渲染 API,按需提高multiplier。 - 源图每边约需 23,622 px((3000 \times 200 / 25.4))才能在 200 DPI 下保持锐利。
客户端试跑(仅强力桌面自测):
- 用缩小画布编辑(如 1,200 × 1,200),再以很高的
multiplier导出。预期不稳定,生产请走服务端。
最佳实践
编辑器性能
- 编辑器里尽量用小图,交互更顺
- 高清地址放在元素自定义字段里
- 仅在导出时换成高清
- 源头图越小,编辑体验越好
素材管理
- 高清 URL 单独存,不要在编辑器里直接加载原图
- 校验分辨率:对照目标 DPI 与物理尺寸(mm)
- 尽早提示用户:上传图不够印刷清晰度时给出警告
- 走 CDN 分发高清资源
导出策略
- 在目标设备实测客户端导出
- 拉长超时:超大服务端任务可能超过数分钟
- 关注内存:大图导出很吃 RAM
- 给进度反馈:长时间任务要有 loading / 轮询状态
故障排查
Canvas 超出浏览器上限
现象:导出失败、崩溃或结果损坏。
处理:
- 降低画布尺寸或
multiplier - 改用服务端(云渲染或 Node)
- 条件允许时用矢量 PDF / SVG
服务端超时
现象:请求未完成就超时。
处理:
- 提高 HTTP 超时(超大任务 5 分钟+)
- 用异步任务 + 轮询 / Webhook,而不是同步长连接
- 降低设计复杂度
- 使用 云渲染 API 的托管队列能力