Window:requestAnimationFrame() 方法
window.requestAnimationFrame() 方法告知瀏覽器你希望執行一個動畫。它請求瀏覽器在下一次重繪之前呼叫一個使用者提供的回撥函式。
回撥函式的呼叫頻率通常會與顯示器的重新整理率匹配。最常見的重新整理率是 60hz (每秒 60 次迴圈/幀),不過 75hz、120hz 和 144hz 也被廣泛使用。為了提高效能和延長電池壽命,當 requestAnimationFrame() 在後臺標籤頁或隱藏的 <iframe> 中執行時,在大多數瀏覽器中會暫停。
注意:如果希望動畫更多幀,你的回撥函式必須再次呼叫 requestAnimationFrame()。requestAnimationFrame() 是一次性的。
警告:請務必始終使用第一個引數(或獲取當前時間的其他方法)來計算動畫在一幀中將進展多少——否則,動畫在高重新整理率螢幕上會執行得更快。有關如何執行此操作的方法,請參見下面的示例。
語法
requestAnimationFrame(callback)
引數
回撥-
當需要為下一次重繪更新動畫時呼叫的函式。此回撥函式將傳遞一個引數。
時間戳-
一個
DOMHighResTimeStamp,指示前一幀渲染的結束時間(基於自 時間原點以來的毫秒數)。時間戳是一個十進位制數字,以毫秒為單位,但最小精度為 1 毫秒。對於Window物件(而不是Workers),它等於document.timeline.currentTime。此時間戳在同一代理上執行的所有視窗(所有同源視窗,更重要的是,同源 iframe)之間共享——這允許在多個requestAnimationFrame回撥之間同步動畫。時間戳值也類似於在回撥函式開始時呼叫performance.now(),但它們絕不是相同的值。當由
requestAnimationFrame()排隊等待的多個回撥在單個幀中開始觸發時,即使在計算每個先前回調的工作負載期間時間已經過去,每個回撥也會收到相同的時間戳。
返回值
一個 unsigned long 整數值,即請求 ID,它唯一地標識回撥列表中的條目。你不應該對其值做任何假設。你可以將此值傳遞給 window.cancelAnimationFrame() 以取消重新整理回撥請求。
警告:請求 ID 通常實現為每個視窗遞增的計數器。因此,即使它從 1 開始計數,它也可能溢位並最終達到 0。雖然不太可能對短期應用程式造成問題,但你應該避免使用 0 作為無效請求識別符號 ID 的哨兵值,而應選擇不可達的值,例如 null。規範沒有指定溢位行為,因此瀏覽器具有不同的行為。當溢位時,值將要麼迴繞到 0,要麼迴繞到負值,要麼失敗並出現錯誤。除非溢位丟擲錯誤,否則請求 ID 也不是真正唯一的,因為對於無限多的回撥,只有有限多個 32 位整數。然而,請注意,在 60Hz 渲染下,每幀呼叫 requestAnimationFrame() 100 次,大約需要 500 天才能達到問題。
示例
在此示例中,一個元素動畫 2 秒(2000 毫秒)。該元素以 0.1px/ms 的速度向右移動,因此其相對位置(以 CSS 畫素為單位)可以根據動畫開始以來經過的時間(以毫秒為單位)透過 0.1 * elapsed 計算。該元素的最終位置在其初始位置右側 200px (0.1 * 2000)。
const element = document.getElementById("some-element-you-want-to-animate");
let start;
function step(timestamp) {
if (start === undefined) {
start = timestamp;
}
const elapsed = timestamp - start;
// Math.min() is used here to make sure the element stops at exactly 200px
const shift = Math.min(0.1 * elapsed, 200);
element.style.transform = `translateX(${shift}px)`;
if (shift < 200) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
以下三個示例說明了設定時間零點(用於計算動畫在每幀中進度的基線)的不同方法。如果你希望與外部時鐘同步,例如 BaseAudioContext.currentTime,可用的最高精度是單個幀的持續時間,在 60Hz 下為 16.67ms。回撥的時間戳引數表示前一幀的結束,因此你新計算的值最早將在下一幀中渲染。
此示例等待直到第一次回撥執行才設定 zero。如果你的動畫在開始時跳到新值,你必須以這種方式構造它。如果你不需要與任何外部事物(例如音訊)同步,則建議使用此方法,因為某些瀏覽器在首次呼叫 requestAnimationFrame() 和首次呼叫回撥函式之間存在多幀延遲。
let zero;
requestAnimationFrame(firstFrame);
function firstFrame(timestamp) {
zero = timestamp;
animate(timestamp);
}
function animate(timestamp) {
const value = (timestamp - zero) / duration;
if (value < 1) {
element.style.opacity = value;
requestAnimationFrame((t) => animate(t));
} else element.style.opacity = 1;
}
此示例使用 document.timeline.currentTime 在首次呼叫 requestAnimationFrame 之前設定零值。document.timeline.currentTime 與 timestamp 引數對齊,因此零值等同於第 0 幀的時間戳。
const zero = document.timeline.currentTime;
requestAnimationFrame(animate);
function animate(timestamp) {
const value = (timestamp - zero) / duration; // animation-timing-function: linear
if (value < 1) {
element.style.opacity = value;
requestAnimationFrame((t) => animate(t));
} else element.style.opacity = 1;
}
此示例使用 performance.now() 進行動畫,而不是回撥的時間戳值。你可能使用此方法來實現稍微更高的同步精度,儘管額外的精度程度是可變的,並且提升不大。
注意:此示例不允許你可靠地同步動畫回撥。
const zero = performance.now();
requestAnimationFrame(animate);
function animate() {
const value = (performance.now() - zero) / duration;
if (value < 1) {
element.style.opacity = value;
requestAnimationFrame((t) => animate(t));
} else element.style.opacity = 1;
}
規範
| 規範 |
|---|
| HTML # dom-animationframeprovider-requestanimationframe |
瀏覽器相容性
載入中…