超级面板
文章目录
最新文章
最近更新
文章分类
标签列表
文章归档

window.requestAnimationFrame

对于动画而言,设置一个合适的更新周期是非常重要的,更新周期越短,动画则越平滑流畅,但是,间隔时间过短,则会对系统资源的消耗增加,另一方面,大多数显示器的刷新频率是 60HZ ,更新速度超过这个数值并没有什么意义,因为显示器不会刷新,因此动画的最佳执行时间间隔是 1000ms/60 约等于 16.7ms 执行一次更加合适。

而为 setTimeoutsetInterval 设置这样的更新频率并不有效,它们的运行时间并不准确,如果它们之前有耗时的同步任务,则他们的执行时机会被大大的推迟,并不能做到准去的执行。

而 HTML5 新引入的专门处理动画的定时器 requestAnimationFrame 则是专门处理动画的周期执行函数,和 setTimeoutsetInterval 相比,不需要指定运行的时间间隔,由浏览器决定函数的执行时机,可以保证执行效率和执行时效的需要。

特点

setTimeoutsetInterval 相比较, requestAnimationFrame 的方式的特点如下:

  • requestAnimationFrame 是浏览器专门提供给处理动画用的 API,浏览器会对其进行专门的优化,在运行的时候,如果页面未激活,动画会自动被暂停,节约资源;
  • 如果元素不可见,requestAnimationFrame 将不会进行回流和重绘;
  • requestAnimationFrame 会把每一次执行的所有 DOM 操作集合起来,在一个渲染周期内完成重绘,从而使动画效果更加的流畅。

使用

requestAnimationFrame 的使用和 setTimeout 类似,只不过不需要设置时间间隔

var step = function(timer) {
//do sth;
}
var requestID = requestAnimationFrame(step);

requestAnimationFrame 不能设置执行的时间间隔,调用一次则执行一次,如果需要类似于 setInterval 的多次执行,可在回调函数中调用 requestAnimationFrame , 这样回调函数就会自动在下一个合适的时机被调用:

var requestID = 0;
var step = function(timer) {
//do sth;
requestID = requestAnimationFrame(step);
}
requestID = requestAnimationFrame(step);

返回的 requestID 是一个长整型非零值,作为每个 requestAnimationFrame 的唯一的标识符,可以将该值作为参数传给 window.cancelAnimationFrame() 来取消这个回调函数,如果多次执行 requestAnimationFrame 时,在每次执行的时候都需要更新 requestID ,否则无法取消 :

var i = 0
var requestID = 0;
var step = function(timer) {
console.log(i);
i ++;
if (i > 100) {
cancelAnimationFrame(requestID)
}
requestID = requestAnimationFrame(step);
}
requestID = requestAnimationFrame(step);

浏览器兼容

首先看一下浏览器的兼容性:
requestAnimationFrame兼容性

从图上可以看出,支持度已经比较高了,基本可以放心使用,对于不支持这个函数的浏览器,可以使用浏览器专用方法或者 setTimeout 替代:

(function() {
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']);
}
window.requestAnimationFrame = window.requestAnimationFrame || function(callback) {
window.setTimeout(callback, 1000 / 60);
};
}());

上面这个实现比较简单,主要是用各个浏览器的私有方法,并对不存在该私有方法的浏览器采用 setTimeout 实现,其实现实际上和 requestAnimationFrame 并不一致,但已经可以保证应用在大多数的场合之下了,更加完善的被 Erik Möller 实现的 polyfill 代码如下:

// 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;
}
}());

参考