SVG和Canvas,前端里的两支“画笔”,用的时候怎么选择?

打开一个数据大屏,满屏是跳动的折线图;打开一个在线白板,你可以随手涂鸦;打开一个网页小游戏,角色在屏幕上跑来跑去......这些画面背后,往往是前端里的两支"画笔":SVG 和 Canvas。

很多刚接触前端的朋友会疑惑:它们都能画图,到底有什么区别?什么时候该用哪个?


一、先打个比方:剪纸贴画 vs 黑板粉笔

想象你要在墙上布置一幅画,有两种做法:

做法一:剪纸贴画。 你剪出一个圆形、一个三角形、一段彩带,一张张贴到墙上。每一张纸片都是独立的。想把圆挪个位置?揭下来重新贴就行。想把三角形换成红色?直接换一张。别人问"这块是什么",你还能指着说"这是太阳"。

做法二:在黑板上用粉笔画。 你拿起粉笔,唰唰几笔画出圆和三角形。画完以后,黑板上只有一片粉笔灰,黑板并不知道哪里是圆、哪里是三角形。想挪动那个圆?对不起,只能擦掉重画。

这两种做法,基本就对应了:

  • SVG ≈ 剪纸贴画:每个图形都是独立存在、可以单独操作的"对象"。
  • Canvas ≈ 黑板粉笔:画上去的只是像素,画完就"忘了"自己画过什么。

记住这个比喻,后面的所有区别几乎都能从这里推出来。


二、SVG:用"文字描述"画出来的图

1. 它是什么

SVG 的全称是 Scalable Vector Graphics,可缩放矢量图形。名字里有两个关键词:

  • 矢量(Vector):图形不是由一个个像素点拼起来的,而是用数学方式描述的。比如"圆心在 (50, 50),半径 40"。
  • 可缩放(Scalable):既然是数学描述,那放大十倍也只是把数字乘以十,依旧清晰锐利,不会出现马赛克。

2. 长什么样

SVG 本质上是一段 XML 格式的文字,和 HTML 长得很像:

html 复制代码
<svg width="200" height="200">
  <circle cx="100" cy="100" r="80" fill="orange" />
  <rect x="70" y="70" width="60" height="60" fill="skyblue" />
  <text x="100" y="190" text-anchor="middle">你好 SVG</text>
</svg>

这段代码的意思就像在"口述"一幅画:在中间画一个橙色的圆,上面放一个天蓝色的方块,底下写一行字。浏览器读到这段描述,就把图画出来。

3. 它的特点

(1)每个图形都是网页元素

<circle>、<rect> 和 <div> 一样,都是 DOM 树里的节点。所以你可以:

javascript 复制代码
const circle = document.querySelector('circle');
circle.addEventListener('click', () => {
  circle.setAttribute('fill', 'red');
});

点击圆,它就变红。不需要任何计算,浏览器自动帮你判断"鼠标点中的是不是这个圆"。

(2)可以用 CSS 来装扮

css 复制代码
circle:hover {
  fill: tomato;
  transition: fill 0.3s;
}

鼠标悬停变色、加动画,都和写普通网页一样简单。

(3)无限清晰

在普通屏幕、高清屏、甚至放大到海报尺寸,SVG 都不会糊。这也是为什么网站的 Logo 和图标大量使用 SVG。

(4)对搜索引擎和读屏软件友好

SVG 里的文字是真正的文字,可以被选中、被搜索、被读屏软件朗读,对视障用户更友好。


三、Canvas:一块可以随意挥洒的"像素画布"

1. 它是什么

Canvas 是 HTML5 带来的一个标签 <canvas>。它本身只是一块空白的矩形区域,就像一块空黑板。真正的绘画工作要靠 JavaScript 来完成。

2. 长什么样

html 复制代码
<canvas id="board" width="200" height="200"></canvas>

<script>
  const canvas = document.getElementById('board');
  const ctx = canvas.getContext('2d'); // 拿到"画笔"

  ctx.fillStyle = 'orange';
  ctx.beginPath();
  ctx.arc(100, 100, 80, 0, Math.PI * 2);
  ctx.fill();          // 画一个圆

  ctx.fillStyle = 'skyblue';
  ctx.fillRect(70, 70, 60, 60); // 画一个方块

  ctx.fillStyle = 'black';
  ctx.textAlign = 'center';
  ctx.fillText('你好 Canvas', 100, 190); // 写字
</script>

画出来的效果和上面的 SVG 几乎一样,但过程完全不同:这里是一条条命令,"拿橙色笔、画个圆、涂满;换蓝色笔、画个方块......"

3. 它的特点

(1)画完就只剩像素

执行完上面的代码,Canvas 里并没有"圆"和"方块"这两个东西,只有 200×200 个彩色像素点。你用开发者工具查看,只能看到一个孤零零的 <canvas> 标签。

(2)想交互,得自己动手

如果想实现"点击圆就变红",Canvas 可没法帮你。你得自己做这些事:

  1. 监听整个画布的点击事件;
  2. 拿到鼠标坐标;
  3. 自己算一算这个点在不在圆里(点到圆心距离是否小于半径);
  4. 如果在,把整个画面擦掉,用红色重新画一遍。
javascript 复制代码
canvas.addEventListener('click', (e) => {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  const dist = Math.hypot(x - 100, y - 100);
  if (dist < 80) {
    ctx.clearRect(0, 0, 200, 200);
    // ......用红色重新画所有东西
  }
});

麻烦吗?确实麻烦。但换来的是完全的掌控力 和极高的性能。

(3)放大会模糊

因为是像素,把 Canvas 拉伸放大,图像就会变糊。在高清屏(比如 Retina 屏)上,还需要根据 window.devicePixelRatio 手动把画布的实际像素调大,否则看起来会发虚。这是新手很常踩的坑。

(4)能画"像素级"的东西

Canvas 可以直接读取和修改每一个像素的颜色(getImageData / putImageData)。图片滤镜、马赛克、取色器、视频截帧这类功能,都是它的拿手好戏。


四、核心差异:一张表看明白

对比维度 SVG Canvas
图形本质 矢量,数学描述 位图,像素点
绘制方式 声明式:描述"画面是什么" 命令式:一步步下指令"怎么画"
在页面中的存在 每个图形都是 DOM 节点 只有一个 <canvas> 节点
缩放效果 无限清晰 放大会模糊
事件交互 原生支持,直接绑定到图形上 需自己计算点击位置
修改图形 改属性即可,浏览器自动重绘 通常要擦掉重画
CSS 样式 支持 不支持(只能整体设置画布样式)
图形数量多时 节点越多越卡 依旧流畅
像素级处理 不擅长 擅长
可访问性 / SEO 较好 较差,需额外处理
导出图片 可导出为 SVG 文件,或转为位图 可直接导出 PNG/JPEG

这里面有两点特别值得展开说。

差异一:声明式 vs 命令式

  • SVG 是"点菜":你告诉服务员"我要一份番茄炒蛋",至于怎么炒,厨房(浏览器)负责。
  • Canvas 是"自己下厨":先打蛋,再切番茄,热锅放油......每一步都得你来。

点菜省心,但只能点菜单上有的;自己下厨辛苦,但想怎么做就怎么做。

差异二:性能的"拐点"

这是选型时最关键的考量。

SVG 的每一个图形都是 DOM 节点,浏览器需要为它们维护样式、布局、事件等一大堆信息。图形少的时候,这点开销可以忽略;可一旦图形数量达到成千上万,比如一张有 5 万个散点的图表,页面就会明显卡顿。

Canvas 则不在乎你画了多少东西,它只关心"画布有多大"。画 10 个圆和画 10 万个圆,最终都只是一张图。所以在海量图形、高频刷新的场景下,Canvas 优势明显。

反过来,如果画布很大(比如全屏 4K)、但图形很少,Canvas 每次重绘整个画布反而可能不如 SVG 划算。

可以粗略记成一句话:

图形少、要交互、要清晰 → SVG;图形多、刷新快、要像素操作 → Canvas。


五、它们的联系:不是对手,而是搭档

说了这么多区别,SVG 和 Canvas 其实并不是你死我活的关系,它们之间有不少联系。

1. 都是浏览器提供的原生绘图能力

二者都不需要任何插件,现代浏览器全部支持。它们共同取代了当年的 Flash,成为网页图形的两大支柱。

2. 绘图概念高度相通

学会一个,另一个就会上手很快。它们共享很多相同的概念:

  • 坐标系:都是左上角为原点,x 向右,y 向下;
  • 路径(Path):直线、贝塞尔曲线、圆弧;
  • 填充(fill)和描边(stroke);
  • 渐变、变换(平移、旋转、缩放)、透明度等。

比如 SVG 里画路径用的 d 属性字符串,Canvas 可以通过 Path2D 直接"拿来就用":

javascript 复制代码
const path = new Path2D('M10 10 L100 10 L55 80 Z'); // 和 SVG 的 d 属性写法一样
ctx.fill(path);

3. 可以互相转换

  • SVG → Canvas :把 SVG 当作一张图片,用 ctx.drawImage() 画到 Canvas 上。很多"把网页图表导出为 PNG"的功能就是这么做的。
  • Canvas → SVG:有一些库可以把 Canvas 的绘图命令"录下来",翻译成 SVG 代码。

4. 可以叠在一起用

在实际项目里,二者混用非常常见。比如一个地图应用:

  • 底层用 Canvas 绘制成千上万个地块和道路,保证流畅;
  • 上层用 SVG(或普通 DOM)放少量可点击的标记点、提示框,方便交互。

各取所长,效果最好。

5. 很多图表库两种都支持

像 ECharts 这样的知名图表库,就同时提供 Canvas 和 SVG 两种渲染模式,开发者只需改一个配置项即可切换。这也说明:在上层看来,它们只是同一件事的两种实现方式。


六、到底该怎么选?给你几个典型场景

场景 推荐 原因
网站 Logo、图标 SVG 体积小、无限清晰、能用 CSS 改颜色
简单的柱状图、饼图 SVG 数据量小,交互方便
流程图、组织架构图 SVG 节点要拖拽、点击、编辑
动画插画、路径描边动画 SVG CSS 动画配合起来很优雅
几万个点的散点图、热力图 Canvas 数据量大,SVG 会卡
网页游戏 Canvas 每秒几十帧的高频重绘
在线涂鸦、白板 Canvas 自由绘制,笔迹众多
图片编辑、滤镜、截图 Canvas 需要像素级操作
3D 场景、可视化大屏 Canvas(WebGL/WebGPU) 需要 GPU 加速

最后一行值得多说一句:Canvas 除了常用的 2D 画笔(getContext('2d')),还能通过 getContext('webgl')、getContext('webgpu') 调用显卡进行 3D 绘图。Three.js 这类 3D 库,底层画布就是 Canvas。此外,借助 OffscreenCanvas,还能把繁重的绘图工作搬到 Web Worker 里,避免卡住页面。这些都是 Canvas 在"高性能"方向上的延伸,SVG 并没有对应的能力。


七、一句话总结

  • SVG 像一位有条理的管家:每样东西都登记在册,你说改哪个就改哪个,适合精致、需要交互的图形。
  • Canvas 像一位手速飞快的画家:不记得画过什么,但画得又快又多,适合海量、高频、像素级的绘制。

两者没有谁好谁坏,只有合不合适。下次在项目里要"画点什么"的时候,先问自己三个问题:

  1. 要画的东西多不多?
  2. 需不需要频繁交互?
  3. 需不需要无限放大或像素级处理?

想清楚这三点,选择哪支画笔,答案自然就出来了。

相关推荐
飘尘1 小时前
从"算子"到"AI Infra":大模型背后看不见的那群人在忙什么
人工智能·算法·面试
ShineWinsu1 小时前
对于Redis:主从复制的解析
linux·数据库·c++·redis·缓存·面试·主从复制
计算机魔术师2 小时前
DeepSeek 据报道接近完成至少 800 亿元融资,腾讯与宁德时代参与
前端
可乐ea2 小时前
多模型编排的三层:框架、模型路由与提供商路由
前端·网络·人工智能·ai智能体·多智能体协作·多模型编排·大模型路由
青柠之夏cc4 小时前
前端拖拽功能原生实现,不引入拖拽库完成业务
开发语言·前端·python
ShineWinsu4 小时前
对于Redis:事务的解析
数据库·redis·mysql·缓存·面试·事务·acid
liangshanbo12155 小时前
前端大文件切片上传怎么做?
前端
Sand(ContextGate)5 小时前
Python Agent 测试实战:测试与评估,让 Agent 像传统软件一样可交付
前端·javascript·python·microsoft·ai
广州华水科技5 小时前
单北斗GNSS变形监测系统在城市安全与地质灾害中的应用前景
前端