在 requestAnimationFrame 之前也颤,主要借助 setTimeout/ setInterval 來編寫 JS 動畫埋虹,而動畫的關鍵在于動畫幀之間的時間間隔設置马靠,這個時間間隔的設置有講究卷拘,一方面要足夠小笆制,這樣動畫幀之間才有連貫性除秀,動畫效果才顯得平滑流暢糯累;另一方面要足夠大,確保瀏覽器有足夠的時間及時完成渲染册踩。
大部分顯示器的刷新頻率是60Hz泳姐,即每秒鐘重繪 60
次。大多數瀏覽器都會對重繪操作的頻率加以限制暂吉,使其不超過顯示器的刷新頻率胖秒。一般最平滑流暢的動畫的時間間隔是 1000ms/60,約為 16.7 ms.
時間間隔對于動畫非常重要慕的,但是 setTimeout/ setInterval 的顯著缺陷就是設定的時間并不精確阎肝,它們只是在設定的時間后將相應任務添加到任務隊列中,而任務隊列中如果還有前面的任務尚未執(zhí)行完畢肮街,那么后添加的任務就必須等待风题,這個等待的時間造成了原本設定的動畫時間間隔不準。
requestAnimationFrame 應運而生嫉父,它采用的是系統(tǒng)時間間隔(約16.7ms)沛硅,保持最佳繪制效果與效率,使各種網頁動畫有一個統(tǒng)一的刷新機制绕辖,從而節(jié)省系統(tǒng)資源摇肌,提高系統(tǒng)性能。
MDN關于requestAnimationFrame
:告訴瀏覽器仪际,希望執(zhí)行一個動畫围小,并要求瀏覽器在下次重繪之前使用指定的回調函數更新動畫昵骤。(如果想在瀏覽器下次重繪之前繼續(xù)更新下一幀動畫,那么回調函數自身必須再次調用 window.requestAnimationFrame(回調函數)吩抓,這樣動畫才有很多幀涉茧,否則只有兩幀(初始狀態(tài)算一幀,回調函數執(zhí)行一次的結果算一幀)且在16.7ms內完成就失去了動畫效果 )疹娶。
回調函數會被自動傳入 'DOMHighResTimeStamp'參數(時間戳伴栓,十進制數,單位是毫秒雨饺,最小精度為1ms)钳垮,該參數指示當前被 requestAnimationFrame() 排序的回調函數被觸發(fā)的時間。在同一個幀中的多個回調函數额港,每個都會被傳入一個相同的時間戳饺窿。
示例
<svg width="800" height="600">
<circle cx="50" cy="50" r="10" fill="red" id="myCircle1"></circle>
<circle cx="50" cy="100" r="10" fill="red" id="myCircle2"></circle>
</svg>
<script>
const log=console.log;
const circle1=document.getElementById('myCircle1');
const circle2=document.getElementById('myCircle2');
let start=null;
let count=0;//統(tǒng)計動畫共有多少幀
function step(timestamp,circle) {
if(!start){
start=timestamp;
}
let progress=timestamp-start;
let dx=Math.min(progress/10,600);
circle.setAttribute('cx',dx);
if(progress<6000){
count++;
// 在瀏覽器下次重繪之前繼續(xù)更新下一幀動畫
window.requestAnimationFrame((timestamp)=>{step(timestamp,circle)});
}
else{
log(progress);
log(count);
}
}
window.requestAnimationFrame((timestamp)=>step(timestamp,circle1));//不會阻塞后面語句執(zhí)行
以上代碼執(zhí)行效果就是一個svg繪制的圓形在 6000ms 內水平從左向右勻速移動,最終狀態(tài)如圖1所示移斩,可見動畫整體耗時 progress 為 6006.583 ms肚医,動畫幀數 count 為 359,每幀之間的時間間隔為 progress/count 約為 16.7 ms向瓷。
還有一個要注意的地方肠套,就是 window.requestAnimationFrame(回調函數) 不會阻塞后面語句執(zhí)行,所以下段代碼中通過兩個 window.requestAnimationFrame(回調函數) 語句可以創(chuàng)造兩個同時進行的動畫猖任,如圖2所示你稚。(由于共有了變量 count,所以最終其值為兩個動畫的總幀數朱躺。)
window.requestAnimationFrame((timestamp)=>step(timestamp,circle1));//不會阻塞后面語句執(zhí)行
window.requestAnimationFrame((timestamp)=>step(timestamp,circle2));