requestAnimationFrame 与 setTimeout的性能比较

1. setTimeout

定时器一直是js动画的核心技术,但是它不够精准,因为定时器参数是指time 秒后放入异步队列中等待执行,如果前面有其他任务队列执行时间过长,则会导致动画延迟,效果不精准的问题。

requestAnimation的好处是,采用系统时间间隔(大多数浏览器刷新频率是60Hz, 相当于16.7ms刷新一次), 保持最佳的绘制效率, 不会因为间隔时间过短,造成多度绘制,增加开销。也不会因为时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制。 并且rAF会把每一帧中所有的DOM操作集中起来,再一次重绘或者回流中就完成。

setTimeout动画

setTimeout是通过设置一个间隔时间来不断地改变图像的位置,但是利用setTimeout实现的动画在某些低端机上会出现卡顿,抖动的现象。

setTimeout为什么会出现卡顿?

  • setTimeout的执行时间并不是确定的,在javascript中,setTimeout任务被放入异步队列中等待执行,只有当主线程的任务执行完后,才会去检查该队列里的任务队列是否需要开始执行。因此setTimeout的实际执行时间一般要比其设定的时间晚些。
  • 刷新频率受屏幕分辨率和屏幕尺寸的影响,因此不同设备的屏幕刷新率可能不同,而setTimeout只能设置一个固定的时间间隔,这个时间不一定和屏幕的刷新时间相同。

上述两种情况都会导致setTimeout的执行步调和屏幕的刷新率不一致,从而出现掉帧现象。

为什么步调不一致就会引起掉帧?
因为setTimeout的执行只是在内存中对图像属性进行改变,这个变化必须要到屏幕下次刷新时才会被更新到屏幕上,如果两者步调不一致, 就有可能会导致中间某一帧的操作被跨域过去,而直接更新下一帧的图像。

eg:

假设浏览器刷新频率是60HZ,即16.7ms刷新一次,而setTimeout每隔10ms设置图像向左移动1px,
- 0ms, 屏幕未刷新,waiting...
- 10ms  屏幕未刷新,waiting...   setTimeout开始执行,并设置图像属性left = 1px
- 16.7ms  屏幕开始刷新,屏幕上的图像向左移动了1px, setTimeout 未执行,继续等待中
- 20ms 屏幕未刷新,waiting...setTimeout开始执行,并设置图像属性left = 2px
- 30ms: 屏幕未刷新,waiting...,setTimeout开始执行并设置left=3px;
- 33.4ms:屏幕开始刷新,屏幕上的图像向左移动了3px, setTimeout未执行,继续等待中;

从栗子中可知,屏幕没有更新left=2px的那一帧画面,图像直接从1px的位置跳到了3px的的位置,这就是丢帧现象,这种现象就会引起动画卡顿

2. requestAnimationFrame

在web应用中,实现动画效果的方法比较多,javascript中可以通过定时器setTimeout来实现,css3可以使用transition和animation来实现,html5中的canvas也可以实现。除此之外html5还提供一个专门用于请求动画的API, 即requestAnimationFrame(rAF), 直译就是 “请求动画帧”

rAF与setTimeout相比,最大的优势是由系统来决定回调函数的执行时机. 具体一点,就是系统每次绘制之前会主动调用rAF中的回调函数,如果系统绘制频率是60HZ, 那么回调函数就每16.7ms(1/60s = 1000/60ms)被执行一次, 如果绘制频率是75Hz,那么,这个间隔就变成了 1000/75 = 13.3ms。那就是说,rAF的执行步伐跟着系统的绘制频率走, 它能保证回调函数在屏幕每一次的绘制间隔中只被执行一次, 这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。

下面再对比一个setTimeout和setInterval 。 对于动画来说,如果单帧的执行时间大于间隔时间,用setTimeout比用setInterval更保险,不过最好的方案还是用requestAnimationFrame

3. setInterval

var timer = setInterval(fn, delay)  // setInterval会持续地调用指定的函数,知道timer被取消为止
clearInterval(timer)

timer(setInterve)首次触发后,它的事件进入异步队列中等待执行,当interval再次被触发时(这个时候timer的事件正在执行), 这一次它的事件就被丢弃了。 如果你在一个大的Javascript代码块内把所有的interval回调函数都囤起来的话,其记过就是在Javascript代码块执行完了之后会有一堆的interval事件被执行,而执行的过程中不会有间隔。因此,取代的做法是浏览器情缘先等一等,以确保在一个interval进入队列的时候队列中没有别的interval

setInterval(function(){
	// 很长的代码块
},10)

取代方法: (用setTimeout取代setInterval)

setTimeout(function(){
	// 很长的代码块
	setTimeout(arguments.callee,10)
},10)

乍看上去,这两段代码在功能上似乎是相同的,可实际上并非如此。setTimeout的代码在前一次的回调执行完后总是至少会有10ms的延时(有 可能会更多,但是绝对不会更少);而setInterval则总是在每10ms的时候尝试执行一次回调,它不管上一次回调是什么时候执行的。

参考文献
requestAnimationFrame详解及对比
javascript线程解释(setTimeout,setInterval你不知道的事)
How JavaScript Timers Work

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值