技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTCSS

选择 Canvas 还是 SVG

所属馆藏
CSS
文件格式
Markdown
原始路径
CSS/30-选择 Canvas 还是 SVG
本文目录10 个章节
  1. 一、Canvas 和 SVG 的基本区别
  2. 二、适用场景
  3. 三、Canvas 和 SVG 的性能差异
  4. 四、定制、交互和可访问性
  5. 五、如何选择 Canvas、SVG 或 WebGL?
  6. 六、G2 使用不同的渲染方案
  7. 七、Canvas 和 SVG 的混合方案
  8. 八、常见误区
  9. 小结
  10. 参考资料

选择 Canvas 还是 SVG

Category(分类): HTML, JavaScript, 可视化 Status: 已整理

Canvas 和 SVG 都可以用于 Web 绘图、数据可视化和交互效果,但它们的绘图模型不同,适用场景也不同。

  • <canvas> 是 HTML 提供的位图绘图表面,通常通过 JavaScript 使用 2D、WebGL 或其他绘图上下文;
  • SVG 是独立的 XML 矢量图形标准,也可以直接嵌入 HTML 文档;
  • Canvas 更接近即时模式绘图,开发者负责每次绘制和更新像素;
  • SVG 更接近保留模式绘图,每个图形通常对应一个可操作的 DOM 节点。

两者都不是“永远更快”或“只能用于某种图表”的技术。选择时应结合图形数量、更新频率、交互方式、可访问性、设备性能和开发成本判断。

一、Canvas 和 SVG 的基本区别

Canvas

Canvas 本身只是一个画布元素,绘图内容不会自动成为 DOM 节点:

<canvas id="canvas" width="600" height="300">
  当前浏览器不支持 Canvas。
</canvas>
const canvas = document.querySelector('#canvas')
const context = canvas?.getContext('2d')

if (context) {
  context.fillStyle = '#2563eb'
  context.fillRect(20, 20, 120, 80)
}

Canvas 的特点:

  • 适合像素级绘制和频繁重绘;
  • 可以绘制大量点、线、图片和粒子;
  • 不为每个图形创建 DOM 节点;
  • 事件命中检测、焦点、键盘操作和可访问性需要开发者自行实现;
  • 可以通过 toBlob()toDataURL() 导出图片;
  • 画布的实际像素尺寸会直接影响内存和绘制成本。

SVG

SVG 使用元素和属性描述图形:

<svg
  width="600"
  height="300"
  viewBox="0 0 600 300"
  role="img"
  aria-label="示例矩形"
>
  <rect x="20" y="20" width="120" height="80" fill="#2563eb" />
</svg>

SVG 的特点:

  • 图形元素通常具有 DOM 节点;
  • 可以使用 CSS、事件、动画和 DOM API;
  • 适合需要单个图形交互、标签和可访问性的场景;
  • 具备矢量缩放能力,适合图标、地图、流程图和中小规模图表;
  • SVG 文档可以被序列化为 XML 文件;
  • 元素数量过多、样式复杂或滤镜过多时,DOM、样式计算和绘制成本会增加。

Canvas 与 SVG 的适用场景示意图

二、适用场景

Canvas 常见适用场景

Canvas 通常适合:

  • 热力图;
  • 大量散点图;
  • 粒子动画;
  • 频繁变化的实时数据;
  • 图片处理和像素级操作;
  • 游戏画面和自定义绘图;
  • 不需要为每个图形提供独立 DOM 交互的场景。

Canvas 的绘图能力更底层,开发者可以直接控制绘制顺序和像素,但也要自己处理:

  • 坐标转换;
  • 缩放和平移;
  • 鼠标和触摸命中检测;
  • 键盘焦点;
  • 文本和图形的可访问性;
  • 高 DPR 屏幕的画布适配。

SVG 常见适用场景

SVG 通常适合:

  • 图标和 Logo;
  • 流程图、组织结构图;
  • 地图和区域选择;
  • 中小规模折线图、柱状图和饼图;
  • 需要单个节点悬停、点击和键盘交互的图表;
  • 需要使用 CSS 控制样式和动画的图形;
  • 需要无损缩放或导出矢量文件的场景。

SVG 的抽象层次更高,图形节点容易维护,也更容易接入浏览器的事件和样式系统。但当节点数量非常多时,DOM 和样式系统的成本可能成为瓶颈。

三、Canvas 和 SVG 的性能差异

网上经常出现“Canvas 性能更好,适合大量数据”的结论,但这个说法过于简单。性能取决于具体场景。

Canvas 主要受这些因素影响

  • 画布 CSS 尺寸和实际像素尺寸;
  • devicePixelRatio
  • 每帧绘制调用数量;
  • 状态切换和路径数量;
  • 图片纹理大小;
  • 是否每帧重绘整个画布;
  • 是否使用离屏 Canvas、WebGL 或分层缓存。

画布的实际像素尺寸可以这样计算:

const ratio = window.devicePixelRatio || 1
const rect = canvas.getBoundingClientRect()

canvas.width = Math.round(rect.width * ratio)
canvas.height = Math.round(rect.height * ratio)

const context = canvas.getContext('2d')
context?.scale(ratio, ratio)

DPR 越高,画布的实际像素数量通常越多,内存和绘制成本也可能增加。

SVG 主要受这些因素影响

  • SVG 元素数量;
  • DOM 层级和样式复杂度;
  • 事件监听器数量;
  • 文本、滤镜、阴影和渐变;
  • 每次更新需要修改的节点数量;
  • 是否频繁触发布局、样式计算和绘制。

Canvas 与 SVG 的性能对比示意图

没有适用于所有场景的性能排名

一般可以这样理解:

  • 少量或中等数量图形、交互要求高:SVG 往往更容易实现和维护;
  • 大量点、频繁刷新、图形不需要独立 DOM:Canvas 往往更合适;
  • 超大规模数据或复杂粒子场景:可能需要 WebGL 或 WebGPU;
  • 大画布不一定适合 SVG,节点数量和绘制复杂度同样重要;
  • Canvas 也不一定适合超大画布,因为高分辨率位图可能消耗大量内存。

小数据量场景中,SVG 可能具有较低的实现和维护成本,缩放时也能保持矢量质量;但“SVG 内存更小、帧率更高”不是固定结论。应该在目标设备和真实数据规模下测试。

四、定制、交互和可访问性

SVG 的交互方式

SVG 图形通常对应 DOM 元素,因此可以直接使用:

  • clickpointerenter 等事件;
  • CSS 类名和伪类;
  • :hover:focus-visible
  • CSS Transitions 和 Animations;
  • aria-labelrole 和键盘焦点;
  • querySelector()、属性修改和 DOM 结构操作。

例如:

.bar {
  fill: #2563eb;
  transition: fill 0.2s ease;
}

.bar:hover,
.bar:focus-visible {
  fill: #f97316;
  outline: none;
}
<rect
  class="bar"
  tabindex="0"
  role="img"
  aria-label="一月销售额 120"
  x="20"
  y="40"
  width="30"
  height="120"
/>

Canvas 的交互方式

Canvas 内部的圆形、矩形和数据点不会自动成为 DOM 元素。要实现交互,通常需要:

  1. 监听 Canvas 的指针事件;
  2. 将页面坐标转换为画布坐标;
  3. 根据几何数据计算命中的图形;
  4. 绘制悬浮状态或提示框;
  5. 通过额外的 HTML 或 ARIA 内容提供可访问信息。

Canvas 也可以通过透明 HTML 层、辅助列表或数据表格补充键盘操作和屏幕阅读器支持。

因此,SVG 的交互入口通常更直接,而 Canvas 在大规模绘制和自定义算法方面自由度更高。

五、如何选择 Canvas、SVG 或 WebGL?

可以先回答下面几个问题:

1. 每个图形是否都需要独立交互?

  • 是:优先考虑 SVG;
  • 否:Canvas、WebGL 或 Canvas + HTML 提示层都可以考虑。

2. 图形数量和刷新频率如何?

  • 少量图形、低频更新:SVG 通常足够;
  • 大量图形、频繁更新:Canvas 可能更合适;
  • 超大规模数据和复杂动画:评估 WebGL 或 WebGPU。

3. 是否需要无损缩放和矢量导出?

  • 是:SVG 更自然;
  • 只需要导出位图:Canvas 可以通过 toBlob() 导出;
  • 两者都需要:可以同时提供 SVG 和 Canvas 导出方案。

4. 是否需要屏幕阅读器和键盘操作?

  • SVG 可以较自然地建立语义节点;
  • Canvas 需要额外提供 HTML、ARIA 或数据表格;
  • 重要数据不能只存在于不可访问的像素中。

5. 页面是否需要混合渲染?

可以将 Canvas 用于大量数据点,将 SVG 或 HTML 用于坐标轴、标签、提示框和交互层。混合方案有时可以同时兼顾绘制性能和交互能力,但实现复杂度也会增加。

六、G2 使用不同的渲染方案

原文示例基于 G2 3.x。从 G2 3.2.7 开始,旧版 G2 支持在 Chart 级别选择 SVG 渲染,Canvas 仍然是默认渲染方式。相同页面可以创建不同 renderer 的图表,但这段 API 不应直接套用到所有新版本 G2。

G2 3.x 的 SVG 示例

示例需要准备容器和依赖:

<div id="mountNode"></div>
import G2 from '@antv/g2'
import DataSet from '@antv/data-set'

const data = [
  {
    name: 'London',
    'Jan.': 18.9,
    'Feb.': 28.8,
    'Mar.': 39.3,
    'Apr.': 81.4,
    May: 47,
    Jun: 20.3,
    Jul: 24,
    Aug: 35.6
  },
  {
    name: 'Berlin',
    'Jan.': 12.4,
    'Feb.': 23.2,
    'Mar.': 34.5,
    'Apr.': 99.7,
    May: 52.6,
    Jun: 35.5,
    Jul: 37.4,
    Aug: 42.4
  }
]

const dataSet = new DataSet()
const view = dataSet.createView().source(data)

view.transform({
  type: 'fold',
  fields: ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.'],
  key: '月份',
  value: '月均降雨量'
})

const chart = new G2.Chart({
  container: 'mountNode',
  forceFit: true,
  height: 500,
  padding: 'auto',
  renderer: 'svg'
})

chart.source(view)
chart
  .interval()
  .position('月份*月均降雨量')
  .color('name')
  .adjust([
    {
      type: 'dodge',
      marginRatio: 1 / 32
    }
  ])

chart.render()

G2 使用 SVG 渲染的示例

这段代码中的 G2DataSet 必须通过依赖导入或对应版本的 CDN 脚本提供。forceFitDataSetrenderer: 'svg' 都属于 G2 3.x API。

新版本 G2 的注意事项

G2 后续版本的 API 和渲染器配置发生过变化,可能使用不同的 autoFit、Renderer 对象或底层渲染引擎配置。升级 G2 时应以对应版本的官方文档为准,不要直接复制 3.x 示例。

同时,“所有图表都支持 Canvas 和 SVG”只能限定在对应版本和官方支持范围内,不能泛化到所有版本、插件和自定义图形。

七、Canvas 和 SVG 的混合方案

实际项目不一定要二选一。例如一个数据可视化页面可以采用:

  • Canvas 绘制大量散点;
  • SVG 绘制坐标轴和少量标记;
  • HTML 绘制 Tooltip 和筛选控件;
  • WebGL 绘制超大规模粒子;
  • 数据表格提供无障碍和精确数据访问。

混合方案需要处理好:

  • 坐标系转换;
  • 事件层级;
  • 滚动和缩放同步;
  • 屏幕阅读器内容;
  • Canvas 和 SVG 的分辨率差异;
  • 多层绘制的性能。

八、常见误区

误区一:Canvas 一定比 SVG 快

错误。Canvas 可能减少 DOM 节点,但高分辨率大画布和频繁全量重绘也可能非常昂贵。

误区二:SVG 只适合少量图形

不完全正确。SVG 可以承载中等规模的图形,但当节点、样式和交互复杂到一定程度时,DOM 和绘制成本可能成为瓶颈。

误区三:Canvas 没有 DOM,所以不需要考虑可访问性

错误。没有 DOM 节点意味着需要额外设计可访问替代内容,而不是不需要考虑可访问性。

误区四:高 DPR 只会让画布更清晰,不影响性能

错误。提高 Canvas 实际像素尺寸可以改善清晰度,但也会增加内存、栅格化和绘制成本。

误区五:G2 的旧版示例可以直接用于当前版本

错误。G2 的 3.x、4.x 和后续版本 API 存在差异,使用前应确认项目依赖版本。

小结

  • Canvas 和 SVG 各有适用场景,没有脱离上下文的性能排名;
  • Canvas 适合像素级处理、频繁重绘和大量图形,但交互与可访问性需要额外实现;
  • SVG 适合中小规模图形、独立节点交互、CSS 样式、矢量缩放和可访问性;
  • 大画布会增加 Canvas 的像素和内存成本,SVG 节点过多也会增加 DOM 和绘制成本;
  • 超大规模数据可以评估 WebGL、WebGPU、数据抽样和分层渲染;
  • 性能比较必须在真实数据量、浏览器、设备和 DPR 下测试;
  • G2 3.2.7renderer: 'svg' 属于旧版 API,升级 G2 时需要查阅对应版本文档;
  • Canvas、SVG、HTML 和 WebGL 可以组合使用,以平衡性能、交互和可访问性。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS