[Canvas优化实验] 离屏画布能带来多大的性能优化?

使用场景

当画面存在重复绘制的多个元素时,将画面上的元素全部绘制到一个离屏的Canvas上,再一次绘制到主canvas上,能够大幅节省绘制时间,优化渲染速度。 但是具体能快多少呢,这篇文章来测试一下

测试场景

我们准备了一块可以平移缩放的画布, 画布背景是通过一次path完成的绘制,在每次画面中更新,没有任何重用,单次渲染为0.0~0.1ms,这是Performance的测量极限,几乎不耗时

生成10000个矩形,随机绘制到坐标10000*10000的画布上,最终绘制效果如下

在在线绘制中,我们过滤一些完全位于屏幕之外的元素,其余绘制到屏幕上,代码如下

world2local 是外部提供的将世界坐标映射到屏幕坐标到工具函数

javascript 复制代码
const MapRange = 10000
const Size = 100
const randomCoords = new Array(10000).fill(0)
  .map(() => [Math.random() * MapRange, Math.random() * MapRange])
  .forEach(rect => {
    const [x, y] = world2local(rect[0], rect[1])
    if (!(x <= -Size * scale || y <= -Size * scale || x >= canvas.width || y >= canvas.height)) {
      ctx.fillRect(x, y, Size * scale, Size * scale);
    }
  })

绘制时间如下,可以看到,画面中多绘制这一项内容会导致单次渲染耗时增加约3ms

在离线绘制中,我们创建一个canvas,然后将全部元素绘制到一个新创建的canvas上,在每次更新的时候将这个canvas上的内容复制到主屏幕上

javascript 复制代码
const MapRange = 10000
const randomCoords = new Array(10000).fill(0).map(() => [Math.random() * MapRange, Math.random() * MapRange])
const offscreenCanvas = document.createElement('canvas')
const Size = 100
const offscreenCtx = offscreenCanvas.getContext('2d')
function drawOffscreen() {
  offscreenCanvas.width = (MapRange + Size) * scale
  offscreenCanvas.height = (MapRange + Size) * scale
  offscreenCtx.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
  randomCoords.forEach(rect => {
    offscreenCtx.fillRect(rect[0] * scale, rect[1] * scale, Size * scale, Size * scale)
  })
}
// 每次缩放需要重新生成对应缩放大小的画布
drawOffscreen()


// 每次平移时只需执行
const [x, y] = world2local(0, 0)
ctx.drawImage(offscreenCanvas, x, y);

绘制时间如下,可以看到,平移画布时单次渲染的成本几乎与空画布相同, 缩放时重新生成离线画布,因此没有优化

结论

离屏Canvas能大幅提高canvas渲染速度,使用离线绘制之后单次重绘成本可以和空画布持平 但是在初次生成的时候仍有较大性能负担,针对一些仅限平移的场景,重绘成本可以接近0

  1. offscreenCanvas的大小是 原始大小*屏幕缩放,如果画布放大到一个很大的值,想要绘制全部元素到画布上,会创建一个巨大的画布,此时内存占用和重绘时间都达到一个不可接受的值,可以采用以下方案。
  • 设定一个最大值裁切画面,只渲染屏幕以及周边一定范围的数据,平移之后再重新渲染,重用画面中重复的范围
  • 分块Canvas,将数据分别绘制到多块Canvas上,只有屏幕画布移动到附近才提前绘制附近的Canvas块
  1. offscreenCanvas在画布缩放时需要更高的成本重绘(因为无法过滤屏幕外数据),实际上可以对在drawImage的时候添加缩放,临时使用缩放前的数据渲染到屏幕上,然后延时更新数据,可以结合以下方法一起更新。
  • 在一个防抖后的函数中执行重绘
  • 使用一个调度器分段执行重绘,完全重绘之后再替换数据
  • 缓冲绘制,通过更多offscreenCanvas提前绘制好多个LOD,缩放时直接替换

完整代码

完整代码可以在我的Github上找到,欢迎学习

相关推荐
漠河愁21 天前
pdf文件渲染到canvas
canvas·pdf.js·fabirc.js
xachary1 个月前
前端使用 Konva 实现可视化设计器(21)- 绘制图形(椭圆)
canvas·konva
x007xyz1 个月前
前端纯手工绘制音频波形图
前端·音视频开发·canvas
甄齐才2 个月前
canvas绘制文本时,该如何处理首行缩进、自动换行、多内容以省略号结束、竖排的呢?
canvas·html2canvas·海报·html转图片·文章分享·dom-to-image·html转image
万水千山走遍TML2 个月前
canvas绘制表格
前端·javascript·vue.js·canvas·canvas绘图·在vue中使用canvas·canvas绘制表格
xachary2 个月前
前端使用 Konva 实现可视化设计器(19)- 连接线 - 直线、折线
javascript·vue·canvas·konva
梦想身高1米82 个月前
canvas.toDataURL后图片背景变成黑色
前端·canvas
x007xyz2 个月前
Fabric.js实时播放视频并扣除绿幕
前端·javascript·canvas
xachary2 个月前
前端使用 Konva 实现可视化设计器(18)- 素材嵌套 - 加载阶段
算法·canvas·konva
LeaferJS2 个月前
LeaferJS 1.0 重磅发布:强悍的前端 Canvas 渲染引擎
前端·canvas