跳到主要内容

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-pathconic-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 编辑大段正文:&lt;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