我們平時實現(xiàn)JavaScript動畫效果時離不開setInterval或者setTimeout函數(shù),這兩個函數(shù)本質(zhì)上相同的。
以setInterval為例,他的作用是以相同的時間間隔執(zhí)行某個操作,這個時間可以自定義,利用這個特性我們就可以讓然也元素跑起來。
可是這個函數(shù)有個毛病,他和顯示器的刷新頻率無法對應(yīng)。比如說顯示器每100毫秒刷新一次,setInterval函數(shù)設(shè)置的間隔執(zhí)行也是100毫秒,可是我們沒有辦法保證顯示頻率刷新的時候setInterval的操作正好被執(zhí)行,雖然它們的相對執(zhí)行時間相同,可絕對時間不一定一致,所以setInterval制作動畫的時候會出現(xiàn)丟幀和動畫效果生硬不連貫等情況。
requestAnimFrame是H5新標(biāo)準(zhǔn)上的東西,在PC瀏覽器上會出現(xiàn)兼容問題,在移動端瀏覽器中可以任意使用。 次函數(shù)會接受一個回調(diào)函數(shù),當(dāng)瀏覽器的顯示頻率刷新的時候,此函數(shù)會被執(zhí)行。
window.requestAnimationFrame(function( time){
//顯示頻刷新的時候被執(zhí)行
});
回調(diào)函數(shù)有一個參數(shù),是一個相對的時間毫秒值,表示當(dāng)前的刷新時間。
requestAnimationFrame的回調(diào)函數(shù)并不能被重復(fù)調(diào)用,這點和setInterval不同,它和setTimeout類似,回調(diào)函數(shù)只能被調(diào)用一次,只不過setTimeout可以自定義調(diào)用時間, requestAnimationFrame的調(diào)用時間則是跟著系統(tǒng)的刷新頻率走的,所以在實現(xiàn)動畫的時候,setTimeout比requestAnimationFrame更加靈活, requestAnimationFrame比setTimeout表現(xiàn)效果更加優(yōu)秀。
以在3000毫秒內(nèi)移動1500px距離的動畫為例
setTimeout的實現(xiàn)方式
<div id="div" style="width:100px; height:100px; background-color:#000; position: absolute;left:0; top:0;">
</div>
<script type="text/javascript">
let divEle = document.getElementById("div");
const distance = 1500;
const timeCount = 3000;
const intervalTime = 10;
let runCount = timeCount / intervalTime;
let moveValue = distance / runCount;
function handler() {
let left = parseInt(divEle.style.left);
if(left >= distance) {
return;
}
divEle.style.left = left + moveValue;
window.setTimeout(handler, intervalTime);
}
window.setTimeout(handler, intervalTime);
</script>
以上代碼通過setTimeout每10毫秒為間隔時間改變一次元素的位置以實現(xiàn)元素的動畫效果, 當(dāng)然, 可以通過改變這個間隔時間來微調(diào)動畫效果,可是你永遠(yuǎn)沒有辦法確定最優(yōu)方案,因為它總會和刷新頻率存在交叉。
通過requestAnimationFrame我們可以給出更好的解決方案
<div id="div" style="width:100px; height:100px; background-color:#000; position: absolute;left:0; top:0;">
</div>
<script type="text/javascript">
let divEle = document.getElementById("div");
const distance = 1500;
const timeCount = 3000;
function handler( time ) {
if(time > timeCount) {
time = timeCount;
}
divEle.style.left = time * distance / timeCount;
window.requestAnimationFrame( handler );
}
window.requestAnimationFrame( handler );
</script>
如果setTimeout,handler函數(shù)也會被遞歸的重復(fù)調(diào)用,只是它的調(diào)用和顯示的刷新頻率是一致的,因此動畫效果更加順滑自然,也能找到性能和效果的最佳均衡點,得到最有的解決方案。