requestAnimationFrame/cancelAnimationFrame——性能更好的js動畫實現方式

用js來實現動畫,咱們通常是藉助setTimeout或setInterval這兩個函數,css3動畫出來後,咱們又可使用css3來實現動畫了,並且性能和流暢度也獲得了很大的提高。可是css3動畫仍是有很多侷限性,好比不是全部屬性都能參與動畫、動畫緩動效果太少、沒法徹底控制動畫過程等等。因此有的時候咱們仍是不得不使用setTimeout或setInterval的方式來實現動畫,但是setTimeout和setInterval有着嚴重的性能問題,雖然某些現代瀏覽器對這兩函個數進行了一些優化,但仍是沒法跟css3的動畫性能相提並論。這個時候,就該requestAnimationFrame出馬了。css

requestAnimationFrame 是專門爲實現高性能的幀動畫而設計的一個API,目前已在多個瀏覽器獲得了支持,包括IE10+,Firefox,Chrome,Safari,Opera等,在移動設備上,ios6以上版本以及IE mobile 10以上也支持requestAnimationFrame,惟一比較遺憾的是目前安卓上的原生瀏覽器並不支持requestAnimationFrame,不過對requestAnimationFrame的支持應該是大勢所趨了,安卓版本的chrome 16+也是支持requestAnimationFrame的。ios

在這裏插入圖片描述


requestAnimationFrame 比起 setTimeout、setInterval的優點主要有兩點:

一、requestAnimationFrame 會把每一幀中的全部DOM操做集中起來,在一次重繪或迴流中就完成,而且重繪或迴流的時間間隔牢牢跟隨瀏覽器的刷新頻率,通常來講,這個頻率爲每秒60幀。css3

二、在隱藏或不可見的元素中,requestAnimationFrame將不會進行重繪或迴流,這固然就意味着更少的的cpu,gpu和內存使用量。web


注意:

像setTimeout、setInterval同樣,requestAnimationFrame是一個全局函數。調用requestAnimationFrame後,它會要求瀏覽器根據本身的頻率進行一次重繪,它接收一個回調函數做爲參數,在即將開始的瀏覽器重繪時,會調用這個函數,並會給這個函數傳入調用回調函數時的時間做爲參數。因爲requestAnimationFrame的功效只是一次性的,因此若想達到動畫效果,則必須接二連三的調用requestAnimationFrame,就像咱們使用setTimeout來實現動畫所作的那樣。requestAnimationFrame函數會返回一個資源標識符,能夠把它做爲參數傳入cancelAnimationFrame函數來取消requestAnimationFrame的回調。怎麼樣,是否是也跟setTimeout的clearTimeout很類似啊。chrome

因此,能夠這麼說,requestAnimationFrame就是一個性能優化版、專爲動畫量身打造的setTimeout,不一樣的是requestAnimationFrame不是本身指定回調函數運行的時間,而是跟着瀏覽器內建的刷新頻率來執行回調,這固然就能達到瀏覽器所能實現動畫的最佳效果了。瀏覽器

目前,各個支持requestAnimationFrame的瀏覽器有些仍是本身的私有實現,因此必須加前綴,對於不支持requestAnimationFrame的瀏覽器,咱們只能使用setTimeout,由於二者的使用方式幾近相同,因此這二者的兼容並不難。對於支持requestAnimationFrame的瀏覽器,咱們使用requestAnimationFrame,而不支持的咱們優雅降級使用傳統的setTimeout。把它們封裝一下,就能獲得一個統一兼容各大瀏覽器的API了。性能優化

(function() {
	var lastTime = 0;
	var vendors = ['webkit', 'moz', 'ms', 'o'];
	for( var x=0 ; x<vendors.length ; ++x){
		if ( window.requestAnimationFrame && window.cancelAnimationFrame ) {
   			break;
		}
		window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
		window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
	}
	if(!window.requestAnimationFrame){ 
		window.requestAnimationFrame = function(callback, element) {
			var currTime = new Date().getTime();
			var timeToCall = Math.max(0, 16 - (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); 
		};
	}
}());
複製代碼

//簡化版本bash

window.requestAnimationFrame=window.requestAnimationFrame ||
	window.webkitRequestAnimationFrame ||
	window.mozRequestAnimationFrame ||
	window.msRequestAnimationFrame ||
	window.oRequestAnimationFrame ||
	function( callback ){
		//爲了使setTimteout的儘量的接近每秒60幀的效果
		window.setTimeout(callback,1000/60);
	}
 
window.cancelAnimationFrame=window.cancelAnimationFrame ||
	Window.webkitCancelAnimationFrame ||
	window.mozCancelAnimationFrame ||
	window.msCancelAnimationFrame ||
	window.oCancelAnimationFrame ||
	function( id ){
		//爲了使setTimteout的儘量的接近每秒60幀的效果
		window.clearTimeout( id );
	}
複製代碼

下面舉個簡單的例子來講明怎麼運用requestAnimationFrame進行動畫,下面的代碼會將id爲demo的div以動畫的形式向右移動到300px
<div id="demo" style="position:absolute; width:100px; height:100px; background:#ccc; left:0; top:0;"></div>
<script>
	var demo = document.getElementById('demo');
	function rander(){
		demo.style.left = parseInt(demo.style.left) + 1 + 'px'; //每一幀向右移動1px
	}
	requestAnimationFrame(function(){
		rander();
		//當超過300px後才中止
		if(parseInt(demo.style.left)<=300){			
			requestAnimationFrame(arguments.callee);
		}
	});
</script>
複製代碼
相關文章
相關標籤/搜索