5月27日 15:48

SVG 和 Canvas 有什么区别?什么时候用哪个?

SVG 和 Canvas 都能在网页上绘图,但底层原理完全不同:

SVG 是矢量图,基于 DOM。 每个图形都是一个独立的 DOM 节点,可以用 CSS 设样式、用 JS 绑事件,浏览器负责渲染和重绘。放大缩小永远清晰,因为存的是数学描述而非像素点。

Canvas 是位图,基于像素。 你通过 JS 调用绘图 API 在画布上逐像素绘制,画完浏览器就不管了——它不记得你画了什么,只保存最终那张位图。要改东西就得清空重画。

这个根本差异决定了它们在性能、交互、可访问性上的所有不同。

7 个维度的详细对比

1. 渲染机制

SVG 绘制的每个元素都保留在 DOM 树中。你画了一个圆,它就是一个 <circle> 节点,属性改了浏览器自动重绘。

Canvas 只有一个 <canvas> 标签,内部全靠 JS 维护状态。你画了一万个圆,DOM 里还是只有一个元素。

html
<!-- SVG:每个图形是独立节点 --> <svg width="200" height="200"> <circle cx="100" cy="100" r="50" fill="red" /> </svg> <!-- Canvas:只有一个标签,图形全靠 JS 绘制 --> <canvas id="c" width="200" height="200"></canvas> <script> const ctx = document.getElementById('c').getContext('2d'); ctx.beginPath(); ctx.arc(100, 100, 50, 0, Math.PI * 2); ctx.fillStyle = 'red'; ctx.fill(); </script>

2. 性能表现

这是面试中最常被追问的点:

  • SVG 性能与元素数量强相关。 元素少的时候没问题,一旦到几千个节点,DOM 操作和重绘的开销急剧上升。实际测试中,3000-5000 个元素是个常见的瓶颈区间。
  • Canvas 性能与画布尺寸强相关,与绘制对象数量关系不大。 画一万个点和画一百个点,只要画布尺寸相同,帧率差异不大。Canvas 不维护对象模型,所以没有 DOM 操作的开销。

简单判断:图形少用 SVG,图形多用 Canvas。

3. 事件交互

SVG 天然支持 DOM 事件。每个 <circle><path> 都能直接绑 clickmouseenter,跟操作普通 HTML 元素一样。

Canvas 只有整个画布能接收事件。想判断点击了哪个图形,需要自己做碰撞检测——记录每个图形的坐标和边界,点击时遍历计算。库如 Konva.js、Fabric.js 帮你在 Canvas 上模拟了对象模型,本质上还是在做碰撞检测。

js
// Canvas 碰撞检测示例 canvas.addEventListener('click', (e) => { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // 遍历所有图形判断点击了哪个 for (const shape of shapes) { if (isPointInShape(x, y, shape)) { handleClick(shape); break; } } });

4. 缩放与分辨率

SVG 是矢量图,任意缩放都不失真,特别适合需要高分辨率输出的场景(打印、Retina 屏)。

Canvas 是位图,放大就模糊。要做高清适配,需要手动处理设备像素比(devicePixelRatio),设置更大的画布尺寸再 scale 下来:

js
const dpr = window.devicePixelRatio || 1; canvas.width = width * dpr; canvas.height = height * dpr; canvas.style.width = width + 'px'; canvas.style.height = height + 'px'; ctx.scale(dpr, dpr);

5. 可访问性与 SEO

SVG 内容是 DOM 节点,屏幕阅读器可以读取,搜索引擎可以索引文字内容。可以添加 <title><desc> 标签增强无障碍支持。

Canvas 对屏幕阅读器不可见。要支持无障碍,需要额外写 ARIA 标签或在画布外提供替代文本。搜索引擎也无法抓取 Canvas 中的内容。

如果页面内容需要被搜索到,SVG 是更好的选择。

6. 动画实现

SVG 动画可以用 CSS 动画、SMIL 或 JS 操纵 DOM 属性。简单动画实现起来很直观:

css
/* SVG 元素直接用 CSS 动画 */ circle { transition: r 0.3s ease; } circle:hover { r: 60; }

Canvas 动画需要用 requestAnimationFrame 手动实现帧循环,每帧清空画布重绘。复杂度高,但对帧率有完全控制权,适合游戏和粒子系统。

7. 内存管理

SVG 的内存占用随元素数量线性增长,每个节点都是一个完整 DOM 对象。大量 SVG 元素会导致内存压力。

Canvas 内存占用主要取决于画布尺寸(width × height × 4 bytes),与绘制内容复杂度无关。一张 1000×1000 的画布固定占约 4MB 内存。

决策矩阵

场景选 SVG选 Canvas
图标、Logo✅ 矢量清晰,交互方便
简单图表(<3000 数据点)✅ 事件绑定简单,可访问
大数据可视化(万级数据点)✅ 性能稳定
2D 游戏✅ 帧率可控
图像编辑(裁剪、滤镜)✅ 像素级操作
需要缩放/打印✅ 矢量不失真
SEO 重要✅ 可被索引
粒子效果/物理模拟✅ 高性能渲染
需要交互的地图✅ 事件绑定天然支持

混合方案

实际项目中,两者经常配合使用:Canvas 负责高性能渲染(粒子背景、热力图),SVG 负责交互层(标注点、悬浮提示)。很多现代图表库已经内置了这种混合策略。

D3.js 以 SVG 为主,适合中小规模数据可视化;ECharts 默认使用 Canvas,适合大数据量图表;Konva.js 在 Canvas 上模拟了类似 SVG 的对象模型,兼顾性能和交互。

面试回答建议

先一句话说清本质区别:SVG 是基于 DOM 的矢量图,Canvas 是基于像素的位图。 然后从性能、交互、缩放、可访问性四个维度展开。最后给出选择依据:图形少、要交互、要缩放选 SVG;图形多、要性能、要像素控制选 Canvas。这个回答结构清晰,覆盖面试官可能追问的所有方向。

标签:SVG