记一次序列帧动画预渲染解决方案

本文记录了一次解决序列帧动画卡顿问题的过程。通过分析动画原理,发现canvas的频繁重绘是导致卡顿的原因。采用双缓存画布技术进行预渲染,改善了动画流畅性。虽然在iOS端和部分安卓设备上存在内存限制和执行时间问题,但整体提升了用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

主要在最近的免单突袭活动中,利用了大量的动画特效。 但是有一个 钥匙碎裂的动画特别卡, 一开始对问题的定义是不是页面上的序列帧动画太多,导致canvas 占用了 大量内存,因为播放这个序列帧动画的时候, 测试反应 安卓手机 卡的像幻灯片。但是苹果手机却没事。

原理

我们先看下 序列帧动画渲染的原理

1.第一步是将多张图片转成 一张雪碧图 这里其实已经确定了 动画的帧数

2.然后用 canvas 去渲染 每一帧

大家看下这张图:

显示 其实也就是 我们canvas 的容器, 而上面的 1-6 其实就是一张图, 所以序列帧动画的 原理 就是 将1-6 这张图 通过canvas drawImage api 每一帧 进行 截取,然后进行渲染。

表面看是没啥问题, 很简单的逻辑渲染,但是重新绘制的过程,实质上是一个不断刮白-重画的过程。但在屏幕上完成这一系列操作是需要一定时间的,而且屏幕上的图形越复杂,所花的时间就越长,我们肉眼可见的刮白-重画操作,在使用过程中就会让就会直接感觉到屏幕的闪烁。 这就 为啥 在 钥匙爆裂那几张动画卡顿。

为了验证我的猜想, 我将素材中的整体图片使用2x 图 去渲染, 在问题机型表现的卡顿几乎都是没有了,但是 随之带来的一个问题就是图片的质量 就不是很高。 这种对于开发而言, 就是要设计妥协,说图片太大。但是这种对于用户体验来说,就不太好。 所以就有了目前调研的预渲染 canvas , 其实 就是在渲染之前 将这些 canvas 渲染好

双缓存画布

现在我们有一幅图需要放在Canvas中,使用drawImage()方法,有三种写法:

// 将image放到目标canvas指定位置
void ctx.drawImage(image, dx, dy); 

// 将image放到目标canvas指定位置,指定宽高渲染
void ctx.drawImage(image, dx, dy, dWidth, dHeight);

// 将image裁剪之后放到目标canvas指定位置,指定宽高渲染
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); 

第一种方法只是把图片原样放到Canvas中,第二种方法指定宽高就意味着放大或者缩小图片后再放进去,第三种是将图片裁剪后再放大或者缩小放到canvas中,这三种写法操复杂度作依次增加,性能开销也随之增大。

而如果使用离屏渲染(即我们所说的双缓存画布),我们可以预先把图片裁剪成想要的尺寸,然后将该内容保存起来,绘制的时候直接使用第一种写法直接将图片放入Canvas中。

// 在离屏 canvas 上绘制var offscreencanvas = document.createElement('canvas');// 宽高赋值为想要的图片尺寸

offscreencanvas.width = dWidth;

offscreencanvas.height = dHeight;// 裁剪

offscreencanvas.getContext('2d').drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);// 在视图canvas中绘制

viewcontext.drawImage(canvas, x, y); 

双缓存画布技术的核心在于系统需要在内存中开辟一块与当前画面等大的“逻辑屏幕“。我们的画图和动画操作都会先作用于这块”逻辑屏幕“中,当一个操作在这块”逻辑屏幕“上完成之后,再把整块”逻辑屏幕“投放到我们的屏幕上。

测试

我们先看下没有使用预渲染的 canvas 3x 图

很明显在爆裂的这几幁动画,会卡顿, 好像就是卡住了 一样。

使用了 预渲染之后我们看下 播放的视频

背后的 原理 其实很简单 看下这张图:

我们提前做好所有序列帧的在目标canvas 的切割, 在这样的过程之下,我们是无法看到整个图形在屏幕上的重绘过程,从而解决了闪烁问题。就好像看动漫一样,不用双缓存技术,就是画一帧看一帧,肯定会卡顿。而用了双缓存技术,会事先把每一帧画好,不断翻动展示出来。这里大家可以联想到 react 的双缓存 fiber 树哦, 就能理解了。

存在的问题

预渲染canvas 存在的问题有下面两个

1.第一个是 在 ios 端 对canvas 的内存 有限制,如果序列帧的动画过多, 导致canvas 拿不到上下文
2.第二个就是 创建canvas 的js 执行时间 比较久, 在 安卓低端机 表现十分明显
3.关于问题的改进策略 下一篇文章再去讲,留点悬念, 可以关注一波

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值