requestAnimationFrame ,字面含義是請求動畫幀。從API命名來看,和動畫有着密切的關係。其用法跟setTimeout差不多,與setTimeout相比,最大的優勢是 由瀏覽器來決定函數的執行時機 。
形象一點的解釋就是:告訴瀏覽器說 “我這裏有一個函數要執行,你有空了幫忙執行一下”,然後瀏覽器相對比較空閒的時候就給執行了。
用法一:動畫
raf api本身的設計就是用來解決js動畫的性能問題。那麼,爲什麼raf做動畫性能會更好呢?主要原因在於 raf更加智能,它並非加快執行速度,而是適當時候降幀,防止並解決丟幀問題 。當它發現無法維持60fps的頻率時,它會把頻率降低到30fps來保持幀數的穩定。也就是說如果上一次raf的回調執行時間過長,那麼觸發下一次raf回調的時間就會縮短,反之亦然,這也是爲什麼說由瀏覽器來決定執行時機性能會更好。
(function animloop(){
window.requestAnimFrame(animloop);
render();
})();
用法二: 函數節流
在高頻率事件中,爲了防止16ms內發生多次函數執行,使用raf可保證16ms內只觸發一次,這既能保證流暢性也能更好的節省函數執行的開銷。 16ms內函數執行多次沒有意義,因爲顯示器16ms刷新一次,多次執行並不會在界面上有任何顯示。
舉個例子:
var $box = $('#J_num2'),
$point = $box.find('i');
$box.on('mousemove',function(e){
requestAnimationFrame(function(){
$point.css({
top : e.pageY,
left : e.pageX
})
})
})
用法三:CPU節能
raf的另一個特性是:如果頁面不是激活狀態下的話,函數會自動暫停,有效節省了CPU開銷。
在移動端,如果頁面中有自動播放的輪播圖、倒計時或使用 setTimeout/setInterval 來執行任務的定時器。那麼 當app進到後臺或是鎖屏後,webviewcorethread仍然持續佔用CPU,導致耗電 。而使用raf可以很簡單的解決此類問題。
(function(){
var timer;
var $txt = $('#J_num2'),
num = 0;
function play(){
timer = setTimeout(function(){
//使用raf實現非激活狀態下不運行
requestAnimationFrame(function(){
stop();
next();
});
},1000)
}
function stop(){
clearTimeout(timer)
}
function next(){
$txt.text(num++);
play();
}
play();
})();
用法四:分幀初始化
都知道,raf的執行時間約爲16.7ms,即爲一幀。那麼可以使用raf將頁面初始化的函數進行打散到每一幀裏,這樣 可以在初始化時降低CPU及內存開銷 。
很多頁面,初始化加載時,CPU都會有很明顯的波動,就是因爲大量的操作都集中到了一點上。
舉個例子:
頁面中有4個模塊,A、B、C、D,在頁面加載時進行實例化,一般的寫法類似於:
$(function(){
new A();
new B();
new C();
new D();
})
而使用raf可將每個模塊分別初始化,即 每個模塊都有16ms的初始化時間
$(function(){
var lazyLoadList = [A,B,C,D];
$.each(lazyloadList, function(index, module){
window.requestAnimationFrame(function(){new module()});
});
})
用法五:異步化
raf實際是一種異步化的操作,曾經 setTimeout(function(){},0) 一度成爲解決了很多前端疑難雜症的法寶。而現在,可以用raf來代替。
這麼多好處,
用setTimeout和setInterval來實現動畫-缺點
1、對調用動畫的循環機制沒有進行優化
2、即使傳遞毫秒爲單位的參數,也不能打到ms的準確性。因爲JavaScript是單線程的,可能發生阻塞
3、沒有考慮繪製動畫的最佳時機
requestAniamationFrame兼容
requestAnimationFrame 不需要使用者指定循環間隔時間,瀏覽器會基於當前頁面是否可見、CPU的負荷情況等來自行決定最佳的幀速率,從而更合理地使用CPU。
如果想做逐幀動畫,就應該用這個方法,這就要求動畫函數執行會先於瀏覽器重繪動作。通常,被調用的頻率是每秒60次。
回調函數只會傳入一個DOMHighResTimeStamp參數,表示函數隊列被觸發的時間。
requestAnimationFrame返回一個id,用於window.cancelAnimationFrame(id)來取消這個回調函數。
兼容代碼:
window.requestAnimationFrame=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);
};
window.cancelNextRequestAnimationFrame = window.cancelRequestAnimationFrame
|| window.webkitCancelAnimationFrame
|| window.webkitCancelRequestAnimationFrame
|| window.mozCancelRequestAnimationFrame
|| window.oCancelRequestAnimationFrame
|| window.msCancelRequestAnimationFrame
|| clearTimeout;
requestAnimationFrame–輪播動畫
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
*{padding:0;margin: 0}
.outer{width: 100px;height: 100px;overflow:hidden;position: relative;}
.inner{
height:100px;list-style-type:none;width: 400px;
}
.inner:after{height: 0;content: '';clear: both;visibility: hidden;}
.inner li{float: left;width: 100px;height: 100px}
li:nth-child(1){background: red}
li:nth-child(2){background: black}
li:nth-child(3){background: yellow}
li:nth-child(4){background: green}
</style>
</head>
<body>
<div class="outer">
<ul class="inner">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<button>下一步</button>
<script type="text/javascript">
var start = null;
var element = document.querySelector('.inner')
element.style.position = 'absolute';
var cur=0,mm=100,timeid
function step(timestamp) {
if (!start) start = timestamp;
var progress = timestamp - start;
if(cur==3){
element.style.left=0;
}else{
element.style.left = -Math.min(progress / 10+cur*100, (cur+1)*100) + 'px';
}
if (progress < 1000) {
timeid=window.requestAnimationFrame(step);
}else{
cur=cur<3?cur+1:0
window.cancelAnimationFrame(timeid)
timeid=null
start=null
setTimeout(auto,1000)
}
}
var btn=document.querySelector('button')
btn.onclick=function(){
auto()
}
function auto(){
if(!timeid){
console.log(cur)
timeid=window.requestAnimationFrame(step);
}
}
</script>
</body>
</html>
參考:http://outofmemory.cn/html/javascript-animation-practice
http://blog.csdn.net/github_37037281/article/details/68957690