SVG 设计:Satori + SVGO
检查时间:2026年10月 — satori 0.63,@resvg/resvg-js 2.6,svgo 4.1。
Satori — 标记 → SVG
npm i satori @resvg/resvg-js
import satori from 'satori'
import { Resvg } from '@resvg/resvg-js'
import { readFile, writeFile } from 'node:fs/promises'
const font = await readFile('./Inter-Bold.ttf') // TTF, OTF, WOFF 或 WOFF2
const svg = await satori(
<div style={{ display: 'flex', width: '100%', height: '100%', alignItems: 'center',
justifyContent: 'center', background: '#0b1020', color: 'white', fontSize: 96 }}>
Đủ 20/20
</div>,
{ width: 1920, height: 1080, fonts: [{ name: 'Inter', data: font, weight: 700, style: 'normal' }] },
)
const png = new Resvg(svg, { fitTo: { mode: 'width', value: 1920 } }).render().asPng()
await writeFile('title.png', png)
- 如果不使用 JSX/React,可以传入纯对象:
{ type: 'div', props: { style: {...}, children: '...' } }。 - 只要有文本,就必须指定字体;越南语需要包含相应字形(如 Inter, Be Vietnam Pro, Noto Sans)的字体。
- 布局:支持 Flexbox、Grid(不支持 subgrid/masonry)和块级布局。它是 CSS 的子集——不支持 JavaScript、动画、外部样式表;所有样式都必须内联。
- 图片:使用
<img src>,其值必须为绝对 URL 或 data URI,且需显式指定width/height。 - 文本默认转换为
<path>(便于移植,但不可选中);设置embedFont: false可保留<text>。 - 表情符号:可使用
graphemeImages或loadAdditionalAsset为缺失的字符提供图片或字体。 - PNG 替代方案:
sharp(Buffer.from(svg)).png()。
SVGO v4 — 优化
npx svgo in.svg -o out.svg
npx svgo -f ./icons -o ./icons-min
// svgo.config.mjs
export default {
multipass: true,
plugins: [
{ name: 'preset-default', params: { overrides: {
// 当 SVG 用于动画或通过 id/class 设置样式时,请保留以下选项:
cleanupIds: false, // 会重命名或移除你的代码所引用的 id
collapseGroups: false, // 会合并你作为整体进行动画的 <g>
mergePaths: false, // 会合并你分别进行动画的路径
convertShapeToPath: false, // 会将 <circle>/<rect> 转换为 <path>(不再保留 r, cx, width)
} } },
'prefixIds', // 当多个 SVG 内联在同一页面时,确保 id 唯一
],
}
- v4 变更:
removeViewBox和removeTitle不再包含在 preset-default 中(如果需要,可按名称添加;保留 viewBox 以保持缩放能力)。removeScriptElement变为removeScripts。仅支持具名导出:import { optimize } from 'svgo';浏览器构建版本为svgo/browser。 - Node.js API:
const { data } = optimize(svgString, { multipass: true, plugins: [...] })。 - 对于静态图标,直接使用默认的
preset-default即可;上述覆盖选项仅对动画或脚本化的 SVG 有用。 - 在优化用于动画的 SVG 后,请检查动画代码中使用的每个选择器在输出中是否仍然存在。