面试被问"SVG 和 Canvas 的区别",答了"一个是矢量一个是位图"就被追问"那具体什么场景用哪个"。这次彻底搞清楚。
本质区别
- SVG:用 XML 描述图形,每个图形都是 DOM 节点
- Canvas:用 JS 在画布上逐像素绘制,画完就忘了
<!-- SVG:每个图形都是 HTML 元素 -->
<svg width="200" height="200">
<circle cx="100" cy="100" r="50" fill="red" />
<rect x="10" y="10" width="80" height="80" fill="blue" />
</svg>
<!-- Canvas:用 JS 画 -->
<canvas id="canvas" width="200" height="200"></canvas>
<script>
const ctx = document.getElementById('canvas').getContext('2d')
ctx.fillStyle = 'red'
ctx.beginPath()
ctx.arc(100, 100, 50, 0, Math.PI * 2)
ctx.fill()
ctx.fillStyle = 'blue'
ctx.fillRect(10, 10, 80, 80)
</script>
核心差异
| SVG | Canvas | |
|---|---|---|
| 渲染方式 | 矢量(XML 描述) | 位图(逐像素绘制) |
| DOM 集成 | 每个图形是 DOM 节点 | 不在 DOM 中 |
| 事件绑定 | 可以直接绑定 click 等事件 | 不能直接绑定,需要手动计算坐标 |
| 放大缩小 | 不失真(矢量) | 会模糊(需要重绘) |
| 性能 | 图形多时慢(DOM 节点多) | 大量图形时快(直接操作像素) |
| 动画 | CSS 动画 / SMIL | requestAnimationFrame |
| 导出 | 天然就是文件 | 可以 toDataURL() 导出 PNG |
什么时候用 SVG
- 图标:大部分图标库(Font Awesome、Material Icons)都用 SVG
- 图表:ECharts、D3.js、Chart.js 的简单图表
- 简单动画:loading 动画、路径动画
- 需要交互的图形:每个元素可以绑定事件
<!-- SVG 图标 -->
<svg viewBox="0 0 24 24" width="24" height="24">
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"
fill="none" stroke="currentColor" stroke-width="2"/>
</svg>
什么时候用 Canvas
- 大量图形:粒子系统、数据可视化(几万个数据点)
- 游戏:2D 游戏画面
- 图片处理:滤镜、裁剪、水印
- 实时绘制:画板、签名板
// Canvas 画板
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
let drawing = false
canvas.addEventListener('mousedown', () => { drawing = true })
canvas.addEventListener('mouseup', () => { drawing = false })
canvas.addEventListener('mousemove', (e) => {
if (!drawing) return
const rect = canvas.getBoundingClientRect()
ctx.lineTo(e.clientX - rect.left, e.clientY - rect.top)
ctx.stroke()
})
前端图表库的选择
| 库 | 渲染方式 | 适合场景 |
|---|---|---|
| ECharts | Canvas(默认)/ SVG | 大数据量、复杂图表 |
| Chart.js | Canvas | 简单图表、轻量 |
| D3.js | SVG | 高度定制化可视化 |
| Recharts | SVG | React 项目、简单图表 |
| AntV G2 | Canvas / SVG | 阿里系、企业级 |
数据量大(>1000 个数据点)选 Canvas 渲染的库,数据量小且需要交互选 SVG。
Canvas 高清屏适配
Retina 屏(devicePixelRatio = 2)直接画会模糊:
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const dpr = window.devicePixelRatio || 1
// 放大画布
canvas.width = canvas.clientWidth * dpr
canvas.height = canvas.clientHeight * dpr
// 缩放上下文
ctx.scale(dpr, dpr)
// 现在画出来的就是高清的
ctx.fillRect(0, 0, 100, 100)
总结
SVG 适合少量图形、需要交互和动画的场景(图标、简单图表);Canvas 适合大量图形、高性能绘制场景(游戏、粒子、图片处理)。图表库选 ECharts(Canvas)或 D3(SVG)基本不会错。