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> 都能直接绑 click、mouseenter,跟操作普通 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 下来:
jsconst 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。这个回答结构清晰,覆盖面试官可能追问的所有方向。