SVG 和 Canvas 到底有什么区别

2 阅读2分钟

面试被问"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>

核心差异

SVGCanvas
渲染方式矢量(XML 描述)位图(逐像素绘制)
DOM 集成每个图形是 DOM 节点不在 DOM 中
事件绑定可以直接绑定 click 等事件不能直接绑定,需要手动计算坐标
放大缩小不失真(矢量)会模糊(需要重绘)
性能图形多时慢(DOM 节点多)大量图形时快(直接操作像素)
动画CSS 动画 / SMILrequestAnimationFrame
导出天然就是文件可以 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()
})

前端图表库的选择

库渲染方式适合场景
EChartsCanvas(默认)/ SVG大数据量、复杂图表
Chart.jsCanvas简单图表、轻量
D3.jsSVG高度定制化可视化
RechartsSVGReact 项目、简单图表
AntV G2Canvas / 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)基本不会错。