目的
由於前端的瀏覽器兼容性問題,很多比較優雅的動畫往往只在某些瀏覽器中可以使用,而在其它瀏覽器無效。所有瀏覽器都兼容的requestAnimationFrame
便是用來解決這種問題。它能夠按照規定的"路線"對幀進行更新,從而實現動畫效果。
函數原型
window.requestAnimationFrame(callback)
callback(DOMHighResTimeStamp)
, 參考MDN,該函數告訴瀏覽器執行一個動畫,並且要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。
該方法需要傳入一個回調函數作爲參數,回調函數會在瀏覽器下一次重繪之前執行。回調函數的參數爲DOMHighResTimeStamp,十進制數值,表示網頁加載完成後至今的毫秒數(即執行回調函數時的時間戳),與performace.now()
的返回值相同。
具體來說,回調函數的內容一般是
callback(timestamp){
根據timestamp計算需要更新的屬性值
更新屬性值
獲得動畫執行進度 ( 經歷的時間值 = timestamp - 起始時間戳 )
還需要繼續動畫 -> requestAnimationFrame(callback)
不需要繼續動畫 -> return
}
調用requestAnimationFrame
後瀏覽器的執行流程爲:
收到動畫請求 -> 調用回調函數,得到下一幀 -> 執行動畫,從當前幀過渡到下一幀 -> (回調函數調用requestAnimationFrame -> 重複) -> 結束
使用回調函數的時間戳參數能夠計算出從開始請求動畫爲止經歷的時間,然後據此執行相應程度的更新,就可以實現動畫效果了。
使用
回調函數的調用間隔很短,因此一般需要在回調函數內部再次調用requestAnimationFrame
,以實現特定時長的動畫,即
function callback(timeStamp){
// ....... 更新元素屬性
requestAnimationFrame(callback);
}
requestAnimationFrame(callback);
如果是均勻的動畫,那麼只需根據執行時間百分比(由回調函數參數計算得到)更新屬性,即在 x% 時刻更新元素的偏移爲 x%·totalOffset。效果圖如下
如果是非均勻的動畫,那麼在回調函數中需要手動計算下一幀的屬性,然後執行更新即可。如下爲使用函數在[0, 6 + 6]
區間的值進行更新的效果圖:
代碼如下:
<!DOCTYPE html>
<html>
<head>
<script>
let totalTime = 850, zeroTimeStamp = 0;
function moveByPercent() {
let moveDistance = 600;
let div = document.getElementById("container");
function callback(timeStamp) {
timeStamp = timeStamp - zeroTimeStamp;
let x = timeStamp;
let y = x / totalTime;
let tmpLeft = y * moveDistance;
div.style.left = (tmpLeft + 20) + 'px';
if (timeStamp < totalTime) requestAnimationFrame(callback);
};
zeroTimeStamp = performance.now();
requestAnimationFrame(callback);
}
function move() {
let div = document.getElementById("container");
// function: a / (1 + e^(-x + b))
let moveDistance = 600;
let a = 16, b = 4;
let x_max = b + 6, y_max = a;
function callback(timeStamp) {
timeStamp = timeStamp - zeroTimeStamp;
let x = timeStamp / totalTime * x_max;
let y = a / (1 + Math.exp(-x + b));
let tmpLeft = y / y_max * moveDistance;
div.style.left = (tmpLeft + 20) + 'px';
if (timeStamp < totalTime) requestAnimationFrame(callback);
};
zeroTimeStamp = performance.now();
requestAnimationFrame(callback);
}
</script>
</head>
<body style="padding: 20px;margin: 0px;">
<input type="button" value="moveByPercent" style="position: absolute;" onclick="moveByPercent()">
<input type="button" value="move" style="position: absolute;margin-left: 200px;" onclick="move()">
<div id="container"
style="width: 300px;height: 300px;box-shadow: gray 1px 1px 6px;margin-top: 40px;position: absolute;background: cyan;">
</div>
</body>
</html>