单位与度量
Ydesign 内部使用什么单位?
Ydesign 画布内部统一使用 CSS 像素(px),并遵循 1pt = 1px、72 dpi 的默认基准。store 里看到的所有与尺寸有关的数值 —— width、height、fontSize、left、top…… —— 都是像素值。
这个选择是为了和 Fabric.js v6 的坐标系一致,也方便和浏览器原生 API(getBoundingClientRect、canvas 宽高等)无缝配合。
切换 UI 显示的单位
虽然底层存的是像素,但你可以通过 store.setUnit 告诉 UI 用什么单位来显示和接收输入(标尺、尺寸面板、打印出血位等都会跟着变):
store.setUnit({
unit: 'mm', // 'px' | 'pt' | 'mm' | 'cm' | 'in'
dpi: 300,
});
切换后:
store.unit→'mm'store.dpi→300- 内置标尺会按毫米显示刻度
- 尺寸面板的输入框会按毫米接收用户输入
store.width/store.height仍然是像素(底层不变,只是 UI 转换了)
📌 所有支持的单位:
px(像素)/pt(磅)/mm(毫米)/cm(厘米)/in(英寸)。当前实现中pt和px按 1:1 处理。
在自定义 UI 里显示单位
Ydesign 暴露了一组工具函数处理像素 ↔ 目标单位的换算,所有内置面板用的是同一套,你的自定义组件也应当优先用它们(避免各算各的导致数值对不上):
import {
pxToUnit,
pxToUnitRounded,
pxToUnitString,
unitToPx,
} from '@ydesign/react-editor/utils/unit';
pxToUnit — 像素 → 数值
pxToUnit({ px: 100, unit: 'mm', dpi: 300 }); // => 8.466...
pxToUnit({ px: 300, unit: 'in', dpi: 300 }); // => 1
pxToUnitRounded — 像素 → 四舍五入数值
适合 UI 展示,避免长串小数:
pxToUnitRounded({ px: 100, unit: 'mm', dpi: 300 }); // => 8.47
pxToUnitRounded({ px: 100, unit: 'mm', dpi: 300, precious: 0 }); // => 8
pxToUnitString — 像素 → 带单位后缀字符串
适合直接丢进标签显示:
pxToUnitString({ px: 2480, unit: 'mm', dpi: 300 }); // => '210mm'
pxToUnitString({ px: 300, unit: 'in', dpi: 300 }); // => '1in'
pxToUnitString({ px: 300, unit: 'px', dpi: 72 }); // => '300px'
unitToPx — 数值 → 像素
接收用户输入时用(比如 "A4 宽度 210mm" → 画布要设多少像素):
const widthPx = unitToPx({ unitVal: 210, unit: 'mm', dpi: 300 });
// => 2480.31...
store.setSize({
width: Math.round(widthPx),
height: Math.round(unitToPx({ unitVal: 297, unit: 'mm', dpi: 300 })),
});
一个完整示例:带单位切换的尺寸输入
import { observer } from 'mobx-react-lite';
import { InputNumber, Select } from 'antd';
import { pxToUnitRounded, unitToPx } from '@ydesign/react-editor/utils/unit';
export const SizeInput = observer(({ store }) => {
const widthInCurrentUnit = pxToUnitRounded({
px: store.width,
unit: store.unit as any,
dpi: store.dpi,
precious: 2,
});
const handleChangeWidth = (val: number | null) => {
if (val == null) return;
const px = unitToPx({ unitVal: val, unit: store.unit, dpi: store.dpi });
store.setSize({ width: Math.round(px), height: store.height });
};
return (
<div style={{ display: 'flex', gap: 8 }}>
<InputNumber value={widthInCurrentUnit} onChange={handleChangeWidth} />
<Select
value={store.unit}
onChange={unit => store.setUnit({ unit, dpi: store.dpi })}
style={{ width: 80 }}
options={[
{ value: 'px', label: 'px' },
{ value: 'mm', label: 'mm' },
{ value: 'cm', label: 'cm' },
{ value: 'in', label: 'in' },
]}
/>
</div>
);
});
导出时的 DPI
客户端导出(store.toDataURL / toBlob / saveAsImage)
客户端导出不读取 store.dpi,而是通过 multiplier 参数决定输出图的清晰度:
// 1920×1080 画布、multiplier=2 → 输出 3840×2160
await store.saveAsImage({
multiplier: 2,
format: 'png',
});
两种常见放大策略:
| 你想要的效果 | 建议做法 |
|---|---|
| 在网页上展示高清缩略图 | multiplier: 2 |
| 印刷级 300dpi 输出 | multiplier: 3 ~ 4 |
| 保持当前 DPI 按屏幕分辨率放大 | enableRetinaScaling: true(自动读取 DPR) |
云端渲染(/api/render/image)
云渲染 API 的 multiplier 参数行为一致,但最高可到 8×(客户端受浏览器内存限制通常 ≤ 4×)。印刷物料推荐:
{
"json": { /* ... */ },
"format": "png",
"multiplier": 4
}
两种"做高清导出"的思路对比
如果你的业务同时存在 72dpi 屏幕预览 + 300dpi 印刷物料两种需求,有两条路可选:
思路 A:画布固定像素 + 导出用 multiplier(推荐)
store.setSize({ width: 2480, height: 3508 })(A4 @ 300dpi 的像素值)- 屏幕预览:
multiplier: 1 - 印刷导出:
multiplier: 1+ 格式改为png即可(画布本身已经是 300dpi 尺寸)
优点:所有元素的像素位置在屏幕和打印上是一致的,不会错位。
思路 B:画布按 72dpi 做 + 导出时靠 multiplier 放大
store.setSize({ width: 595, height: 842 })(A4 @ 72dpi)- 预览:
multiplier: 1 - 印刷:
multiplier: 4(4 × 72 ≈ 288 dpi)
优点:画布小、渲染快、JSON 体积小,适合轻量预览场景。 缺点:细节(小字号、细描边)在放大时可能有像素级偏差。
两种都可行,按产品形态选择即可。
延伸阅读
- 👉 工具函数 · 单位换算 —— 完整 API 与签名
- 👉 Size 面板 —— 自定义尺寸预设 / 单位切换 UI
- 👉 云渲染 API —— 服务端高清渲染
- 👉 场景 & 导入导出 —— 客户端导出选项