跳到主要内容

大尺寸 & 高分辨率导出

大幅面印刷需要很高的输出分辨率,往往超出浏览器 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(与像素上限无关)

可选方案:

  1. 云渲染 API — 托管服务,无需自建
  2. @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 起;大幅面可试 48,务必在目标设备实测:

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 的托管队列能力

相关文档