window.requestAnimationFrame() 方法告诉浏览器您希望执行动画,并请求浏览器调用指定的函数在下一次重绘之前更新动画。该方法将在重绘之前调用的回调作为参数。如果你想做逐帧动画的时候,你应该用这个方法。这就要求你的动画函数执行会先于浏览器重绘动作。通常来说,被调用的频率是每秒60次,但是一般会遵循W3C标准规定的频率。如果是后台标签页面,重绘频率则会大大降低。
浏览器的显示频率一般是16.7ms,我们将setTimeout的间隔时间设为16.7ms,节奏会如下图第一行展示 。但如果我们将间隔时间设为10ms,那么节奏会如下图第二行展示。
举个例子:
国庆北京高速,最多每16.7s通过一辆车,结果,突然插入一批setTimeout的军车,强行要10s通过。显然,这是超负荷的,要想顺利进行,只能让第三辆车直接消失(正如显示绘制第三帧的丢失)。然而,这是不现实的,于是就有了会堵车!
同样的,显示器16.7ms刷新间隔之前发生了其他绘制请求(setTimeout),导致所有第三帧丢失,继而导致动画断续显示(堵车的感觉),这就是过度绘制带来的问题。不仅如此,这种计时器频率的降低也会对电池使用寿命造成负面影响,并会降低其他应用的性能。
这也是为何setTimeout的定时器值推荐最小使用16.7ms的原因(16.7 = 1000 / 60, 即每秒60帧)。
而requestAnimationFrame就是为了解决这个问题而出现的。它所做的事情很简单,就是跟着浏览器的绘制走,如果浏览设备绘制间隔是16.7ms,那它就在这个间隔绘制。如果浏览设备绘制间隔是10ms,它就10ms绘制。这样就不会存在过度绘制的问题,动画不会掉帧,播放自然流畅。
兼容性:
统一的向下兼容方法:
(function() {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || // Webkit中此取消方法的名字变了
window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}());