requestanimationframe_requestAnimationFrame && setTimeout 原理剖析

就目前来讲,web应用实现动画的方式有很多种,排除CSS3自带的transition和 animation动画属性,这里讲一下JavaScript实现动画的其中两种方法,requestAnimationFrame && setTimeout

  • 前言

在开始分析之前,我们要了解几个概念,视觉暂留 屏幕刷新频率

1.视觉暂留

  眼睛的另一个重要特是视觉惰,即光象一旦在视网膜上形成,视觉将会对这个光象的感觉维持一个有限的时间,这种生理现象叫做视觉暂留。对于中等亮度的光刺激,视觉暂留时间约为50ms200ms。当我们看屏幕的时候,虽然你什么也没做,但是屏幕还是以特定的频率在不停刷新,只是这个刷新过程我们肉眼识别到他的细微变化,这就是我们接下来要说的 屏幕刷新频率

2.屏幕刷新频率

  我们日常的显示器,一般频率在60Hz左右,意味着我们的屏幕每1秒需要刷新60次,也就是说每1000ms需要更新60次的屏幕图像,那么我们由此可以得出,屏幕图像更新一次所需要的时间间隔也就是16.7ms(1000/60≈16.7)
  由于人的眼睛具有视觉暂留效应,且暂留时间为50ms200ms,也就是说人在看屏幕的时候,还没等到你的大脑印象消失,电脑屏幕就已经更新了,所以这个间隔让你感觉不到变化。
  那么屏幕刷新频率是不是越大越好?我们可以大胆假设一下,假如我有三个显示器,刷新频率分别为1Hz60Hz200Hz、那么对应的更新周期时间分别为1000ms16.7ms5ms。也就是频率越大,图像更新的间隔就越短,我们看到的画面就会越稳定,当达到一秒更新一次的时候,这个时候我们就能够感觉到明显的屏幕闪烁,带来视觉疲劳。

409372ec34f986fb5837882537efb925.png

3.setTimeout

  setTimeout说白了就是个延时计时器,通过设置固定的时间间隔,从而时间一到,执行相应的回调方法。这个过程不会考虑屏幕刷新频率,换句话讲,它的时间是写死的。那么为什么会存在丢帧现象发生,通俗来说就是为什么使用setTimeout我会感觉到卡顿,画面不稳定。

  1.setTimeout执行的时间与屏幕的刷新频率不一致会导致丢帧现象。我们不考虑异步问题,假设我们现在的屏幕设备是60Hz的刷新频率。那么我们图像的更新周期也就是16.7ms,我现在的动画要求是每10ms往下偏移1px,那么这个丢帧现象是如何产生的?这里我通过一张图来解释一下。

62375934b67f2c78bf8e7a713625da9a.png

分析结果:
  • 第1次重绘(16.7ms):图形偏移到1px

  • 第2次重绘(33.4ms):图形偏移到3px丢失1px偏移;

  • 第3次重绘(50.1ms):图形偏移到4px

  • 第4次重绘(66.8ms):图形偏移到6px丢失1px偏移;

  • 第5次重绘(83.5ms):图形偏移到8px丢失1px偏移;
    ......

实验效果:

640?wx_fmt=gif

  所以根据分析结果以及实验效果,如果setTimeout执行的顺序与屏幕的刷新频率不一致,会造成丢帧现象,从而视觉上带给我们的就是不流畅。

  2.由于JavaScript属于单线程,而setTimeout任务会被放入异步队列,通俗来讲就是它的执行得等一等,具体等什么,不知道,就是想再等等。只有主线程的任务执行完毕之后,才会轮到它去执行,也就是说我虽然设置setTimeout 16.7ms间隔去执行动画属性改变,但是实际运行的时间可能会有所延迟。这个延迟可能会导致执行的时间与屏幕刷新的时间串掉,造成丢帧。

ab2462fb002033afde39ec0490d3ef63.png

分析结果:
  • 第1次重绘(16.7ms):图形未偏移;应该偏移到1px

  • 第2次重绘(33.4ms):图形偏移到1px应该偏移到2px

  • 第3次重绘(50.1ms):图形偏移到2px;应该偏移到3px

  • 第4次重绘(66.8ms):图形偏移到3px应该偏移到4px

  • 第5次重绘(83.5ms):图形偏移到4px应该偏移到5px
    ......所以根据分析结果,我们可以看出,在异步的现象下,会造成一连串的执行差,从而造成丢帧现象。

4.requestAnimationFrame

  官方解释:window.requestAnimationFrame()告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。也就是说requestAnimationFrame它不需要你去手动设置执行间隔时间,它是跟随系统的屏幕刷新频率走的,如果屏幕刷新频率是60Hz,那么它的执行间隔就是16.7ms(1000/60≈16.7),如果屏幕刷新频率是100Hz,那么它的执行间隔就是10ms(1000/100=10),这样就能够保证它的执行与屏幕的刷新频率保持一致,从而避免丢帧现象。
  为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame() 运行在后台标签页或者隐藏的 里时,requestAnimationFrame() 会被暂停调用以提升性能和电池寿命。

5.requestAnimationFrame Vs setTimeout 区别
  • requestAnimationFrame在窗口隐藏的时候,会暂停调用,而setTimeout不会暂停。

  • requestAnimationFrame跟随系统屏幕刷新频率,而setTimeout手动配置,会存在丢帧现象。

代码示例
    var start = 0;
var element = document.getElementById('SomeElementYouWantToAnimate');
element.style.position = 'absolute';
function step() {
start++;
element.style.top = start + 'px';
if (start < 500) {
window.requestAnimationFrame(step);
}
}
window.requestAnimationFrame(step);
6.附件:requestAnimationFrame Github 兼容降级处理

  源地址:https://github.com/darius/requestAnimationFrame

// Adapted from https://gist.github.com/paulirish/1579671 which derived from 
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

// requestAnimationFrame polyfill by Erik Möller.
// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon

// MIT license

if (!Date.now)
Date.now = function() { return new Date().getTime(); };

(function() {
'use strict';

var vendors = ['webkit', 'moz'];
for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
var vp = vendors[i];
window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
window.cancelAnimationFrame = (window[vp+'CancelAnimationFrame']
|| window[vp+'CancelRequestAnimationFrame']);
}
if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
|| !window.requestAnimationFrame || !window.cancelAnimationFrame) {
var lastTime = 0;
window.requestAnimationFrame = function(callback) {
var now = Date.now();
var nextTime = Math.max(lastTime + 16, now);
return setTimeout(function() { callback(lastTime = nextTime); },
nextTime - now);
};
window.cancelAnimationFrame = clearTimeout;
}
}());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值