使用 requestAnimationFrame() 方法让做动画

  • 不要用 setInterval() 或 setTimeout() 来做动画
    setInterval() 与 setTimeout() 并不能提供和制作动画所需的精准计时机制。它们只是让应用程序能在某个大致时间点上运行代码的通用方法而已。
  • 不应主动命令浏览器何时去绘制下一帧动画,这应有浏览器通知你
    在调用 setInterval() 与 setTimeout() 时,开发者会主动告知浏览器绘制下一帧动画的时间。然而,调用者并不知道绘制下一帧动画的最佳时机,浏览器肯定比开发者更了解这个时机。所以应该让浏览器在它觉得可以绘制下一帧动画时通知我们。
function animate(time) {
// Update and draw animation objects

requsetAnimationFrame(animate); // sustain the animation
}

requestAnimationFrame(animate); // start the animation

requestAnimationFrame() 不需要调用者指定帧速率,浏览器会自行决定最佳的帧速率

我们还可以调用 cancelRequestAnimationFrame(long handle) 方法来将原来 requestAnimationFrame() 方法所注册的函数回调给取消执行。requestAnimationFrame() 方法会返回一个 long 型的对象,用作标识回调函数的句柄 (handle),可传入 cancelRequestAnimationFrame() 方法来取消回调函数的执行。

但是 requestAnimationFrame() 的兼容性并不是很好,只兼容到了 IE10 以及以后的版本。我们可以定义一个 “Polyill式方法” 来向下兼容,具体代码如下:

window.requestNextAnimationFrame =
(function() {
var originalWebkitRequestAnimationFrame = undefined,
wrapper = undefined,
callback = undefined,
geckoVersion = 0,
userAgent = navigator.userAgent,
index = 0,
self = this;

// Workaround for Chrome 10 bug where Chrome
// does not pass the time to the animation function

if (window.webkitRequestAnimationFrame) {
// Define the wrapper

wrapper = function(time) {
if (time === undefined) {
time = +new Date();
}
self.callback(time);
};

// Make the switch

originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;

window.webkitRequestAnimationFrame = function(callback, element) {
self.callback = callback;

// Browser calls the wrapper and wrapper calls the callback

originalWebkitRequestAnimationFrame(wrapper, element);
}
}

// Workaround for Gecko 2.0, which has a bug in
// mozRequestAnimationFrame() that restricts animations
// to 30-40 fps.

if (window.mozRequestAnimationFrame) {
// Check the Gecko version. Gecko is used by browsers
// other than Firefox. Gecko 2.0 corresponds to
// Firefox 4.0.

index = userAgent.indexOf('rv:');

if (userAgent.indexOf('Gecko') != -1) {
geckoVersion = userAgent.substr(index + 3, 3);

if (geckoVersion === '2.0') {
// Forces the return statement to fall through
// to the setTimeout() function.

window.mozRequestAnimationFrame = undefined;
}
}
}

return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||

function(callback, element) {
var start,
finish;

window.setTimeout(function() {
start = +new Date();
callback(start);
finish = +new Date();

self.timeout = 1000 / 60 - (finish - start);

}, self.timeout);
};
})();