SVG-vs-Canvas-vs-CSS
判定句:文档节点是真相,还是像素是真相。 SVG 吃前者;Canvas 2D 吃后者。CSS 先问「这是不是布局和合成,根本不需要图 API」。
不要从「矢量看起来更清晰」入题。Retina 上糊不糊是 DPR / viewBox 问题,不是选 SVG 的理由。
1. 从 SVG 侧看三种表面
| 你真正要的 | 优先 | 不要先上 |
|---|---|---|
| 可点选、可对齐、可读屏、可撤销的对象 | SVG 文档 | 把对象画进 canvas 再自己拾取 |
| 少量装饰、随布局缩放、CSS 动画 | SVG 或纯 CSS | 为两个图标开 WebGL |
| 圆角卡片、进度条、简单形状背景 | CSS | <svg> 重做一遍盒模型 |
| 笔刷、涂抹、逐帧滤镜、视频叠字 | Canvas 2D | 在 SVG 里模拟 backing store |
| 万级粒子 / 自定义着色 | WebGL | 给每个粒子一个 <circle> |
SVG 适用,当 改的是树:拖节点改 transform,撤销弹文档命令,命中可以走 DOM 或 isPointInFill。
Canvas 适用,当 改的是像素:这一帧画完,对象语义只活在你自己的 scene 里。详见 Canvas 选型。
CSS 适用,当没有「图形文档」这回事:hover 高亮、clip-path、conic-gradient 进度环,合成器比任何 <svg> 都便宜。
2. 混用是常态
生产里最稳的切法:
SVG:结构、连线、选中框、标签锚点(节点必须活着)
Canvas:笔迹、热力图、底图瓦片烘焙(可以光栅)
DOM overlay:IME、菜单、读屏文本
相机:同一套世界单位 → viewBox / canvas CTM
流程图底图是 SVG 节点,批注墨迹是 Canvas overlay,提交后再把笔画变成 path 进文档——这不是架构不纯,是两种语义。反过来:用 SVG 去存每一笔压感采样点当独立 circle,节点成本会先把你打死,见 矢量白板。
不适用混用:两套相机各算各的 clientX。世界单位必须只有一份,viewBox 是 SVG 的相机;Canvas 用同一 Camera 矩阵。
3. 判定流程
nodeCount 是 要独立更新、可命中的对象,不是磁盘上 path 总数。静态插画一万条 path 仍可能该 SVG;每帧改两千个带滤镜的节点就不该。
export type SurfaceKind = "css" | "svg" | "canvas2d" | "hybrid";
export function chooseSurface(input: {
objectSemantics: "document" | "pixels";
interactiveText: boolean;
pixelInk: boolean;
cssEnough: boolean;
liveNodeCount: number;
}): SurfaceKind {
if (input.cssEnough && input.objectSemantics !== "document") {
return "css";
}
if (input.objectSemantics === "pixels" && !input.interactiveText) {
return "canvas2d";
}
if (input.pixelInk && input.objectSemantics === "document") {
return "hybrid";
}
if (input.interactiveText || input.objectSemantics === "document") {
return input.liveNodeCount > 4_000 ? "hybrid" : "svg";
}
return "css";
}
hybrid 的意思是:文档仍在 SVG(或你的 scene → SVG),光栅层另开 canvas,不要 把文档搬进像素。4_000 是量级信号,滤镜一开阈值立刻下降,测过再翻案。
4. 什么时候不适用 SVG
- 需要像素橡皮、液化、每帧全屏 blur:SVG 会逼你把效果烘成
<image>,最后还是位图,还多一棵假树。 - 对象语义其实没有:大屏上一段不会被点的可视化背景,CSS 或一张静态 SVG
<img>就够,不要 inline 几千节点。 - 需要 IME 编辑大段正文:
<text>不是 textarea。文字进 HTML overlay,图形留 SVG。 - 「无限画布」却用 CSS
transform: scale放大整棵 DOM:命中、线宽、滤镜分辨率一起坏。相机是 viewBox,见 viewport。
5. 失败形态
| 症状 | 选错了什么 |
|---|---|
| 选不中、对不齐、读屏没有图 | 用 Canvas 替代了文档 |
| 拖节点掉帧、layout 爆炸 | 该虚拟化或把密集层光栅,还当每个点都是 DOM |
| 笔刷发糊还对不齐矢量 | 两套相机;或把墨迹硬画成 SVG 却按 CSS 像素存 |
| 进度环写了 80 行 path | 该 CSS conic-gradient / stroke-dasharray |
| 卡片背景一张全屏 SVG 滤镜 | 该 CSS 或预烘焙 PNG |
权威资料
核对日期:2026-08-26