主题
Canvas 指令集
renderCanvas 的输出是一份平台无关的 Canvas 2D 指令脚本——这也是「网页 = 原生同一内核」的关键:桌面端与浏览器渲染走同一份代码,指令流只是另一种序列化产物。
结构
ts
interface CanvasScript {
width: number; // 页面像素宽(mm × scale)
height: number; // 页面像素高
images: Record<string, string>; // id → dataURL(含印章等位图)
commands: CanvasCommand[]; // 按序回放
}坐标已完成 mm → px 换算与 Y 轴翻转(OFD 国标 Y 轴向下,Canvas 亦向下,翻转指将 OFD 顶点系换算到像素系)。
指令全集(23 种)
| op | 参数 | 对应 Canvas API |
|---|---|---|
save | — | save() |
restore | — | restore() |
globalAlpha | alpha | globalAlpha |
beginPath | — | beginPath() |
moveTo | x, y | moveTo |
lineTo | x, y | lineTo |
quadraticCurveTo | cpx, cpy, x, y | quadraticCurveTo |
bezierCurveTo | cp1x, cp1y, cp2x, cp2y, x, y | bezierCurveTo |
closePath | — | closePath() |
fillStyle | style(颜色/渐变/图案 id) | fillStyle |
strokeStyle | style | strokeStyle |
createLinearGradient | id, x1, y1, x2, y2, stops[{pos,color}] | 渐变对象(按 id 引用) |
createRadialGradient | id, x0, y0, cx, cy, r, stops | 渐变对象(x0/y0 为内圆心,cx/cy/r 为外圆) |
createPattern | id, image, matrix? | createPattern("repeat") + setTransform(平铺水印/底纹;image 为瓦片 dataURL,matrix 为像素空间仿射 [a,b,c,d,e,f]) |
fill | — | fill() |
stroke | — | stroke() |
lineWidth | width | lineWidth |
lineCap | cap | lineCap |
lineJoin | join | lineJoin |
miterLimit | limit | miterLimit |
setLineDash | segments[], offset | setLineDash + lineDashOffset |
clip | — | clip() |
drawImage | image(images 中的 id), x, y, w, h, transform? | drawImage(transform 为可选像素空间仿射,印章随页旋转用) |
播放器遇到未知指令会忽略并继续(向前兼容):未来新增指令不会让旧播放器崩溃。
播放器:playCanvasScript
npm 包内置(canvas-player.js):
js
import { playCanvasScript } from "rust-ofd";
await playCanvasScript(canvas, script, window.devicePixelRatio || 1);播放器做了这些事,别自己重复处理:
- 按
width/height × dpr设置画布尺寸(高分屏清晰) - 白底填充(OFD 页面默认白纸)
imageSmoothingEnabled = false(与桌面端渲染一致)- 预加载全部
imagesdataURL 后逐条执行指令 - 兼容
images为 Map 或普通对象两种容器
为什么是 JSON 指令流而不是直接 WASM 里画
指令流可以被任何语言回放(WPF、Skia、PDF 生成器……),也可以在 Worker 中生成、主线程回放。这是把「渲染语义」与「渲染目标」解耦的关键决定。