您好,登錄后才能下訂單哦!
這篇文章主要介紹“requestAnimationFrame定時動畫屏幕刷新率節流的方法”,在日常操作中,相信很多人在requestAnimationFrame定時動畫屏幕刷新率節流的方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”requestAnimationFrame定時動畫屏幕刷新率節流的方法”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
以前,在 JavaScript 中創建動畫基本上就是使用 setInterval()
來控制動畫的執行:
(function () { function updateAnimations() { doAnimation1(); doAnimation2(); // ... } setInterval(updateAnimations, 100); })();
這種定時動畫的問題在于,無法準確知曉循環之間的延時。
無論是 setInterval()
還是 setTimeout()
,都是不能保證時間精度的。作為第二個參數的延時只能保證何時會把代碼添加到瀏覽器的任務隊列,并不能保證添加到隊列就會立即執行。如果隊列前面還有其他任務,那么就要等這些任務執行完再執行。
簡單來講,這里的毫秒延時不是指何時這些代碼會執行,而是指到時候會把回調添加到任務隊列。如果添加到隊列后,主線程還被其他任務占用,那么回調就不會立即執行。
知道何時繪制下一幀是創造平滑動畫的關鍵,所以 setInterval()
和 setTimeout()
的不精確是個大問題。
瀏覽器自身計時器的精度讓這個問題雪上加霜。瀏覽器計時器的精度不足毫秒,最厲害的 Chrome 計時器精度為 4ms。更麻煩的是,瀏覽器又開始對切換到后臺或不活躍的標簽頁中的計時器執行限流,因此即使將時間間隔設為最優,也免不了只能得到近似的結果。
一般計算機顯示器的屏幕刷新率都是 60HZ,基本上意味著每秒需要重繪 60 次。大多數瀏覽器會限制重繪頻率,使其不超過屏幕的刷新率,因為超過屏幕刷新率用戶也感知不到。
所以,實現平滑動畫最佳的重繪時間間隔為 1000ms/60,大約 17 ms。以這個速度重繪可以實現最平滑的動畫,因為這已經是瀏覽器的極限了。
requestAnimationFrame()
這個方法可以通知瀏覽器某些 JavaScript 代碼要執行動畫了,這樣瀏覽器就可以在運行某些代碼后進行適當的優化。
requestAnimationFrame()
這個方法接收一個參數,該參數是一個要在重繪屏幕前調用的函數。這個函數就是修改 DOM 樣式以反映下一次重繪有什么變化的地方。為了實現動畫循環,可以把多個 requestAnimationFrame()
調用串聯起來,就像以前使用 setTimeout()
一樣:
function updateProgress() { var div = document.getElementById("status"); div.style.width = parseInt(div.style.width, 10) + 5 + "%"; if (div.style.left != "100%") { requestAnimationFrame(updateProgress); } } requestAnimationFrame(updateProgress);
因為 requestAnimationFrame()
只會調用一次傳入的函數,所以每次更新用戶界面時需要再手動調用它一次。同時,也需要控制動畫何時停止。結果就會得到非常平滑的動畫。
requestAnimationFrame()
已經解決了瀏覽器不知道 JavaScript 動畫何時開始的問題,以及最佳間隔時間是多少的問題。但是,如果我們想知道自己的代碼實際的執行時間呢?同樣有解決方案。
傳給 requestAnimationFrame()
的函數實際上可以接收一個參數,該參數表示下次重繪的時間。這一點非常重要:requestAnimationFrame()
實際上把重繪任務安排在了未來一個已知的時間點上,而且通過這個參數告訴了開發者,那么基于這個參數,就可以更好地決定如何調優動畫了:
function foo(t) { console.log(t); requestAnimationFrame(foo); } requestAnimationFrame(foo);
const requestID = window.requestAnimationFrame((t) => { console.log(t); }); window.cancelAnimationFrame(requestID);
支持這個方法的瀏覽器實際上會暴露出作為鉤子的回調隊列。所謂鉤子,就是瀏覽器在執行下一次重繪之前的一個點。這個回調隊列是一個可修改的函數列表,包含應該在重繪之前調用的函數。每次調用 requestAnimationFrame()
都會在隊列上推入一個回調函數,隊列的長度沒有限制。
這個回調隊列的行為不一定跟動畫有關。通過 requestAnimationFrame()
遞歸地向隊列中加入回調函數,可以保證每次重繪最多只調用一次回調函數,這是一個非常好的節流工具。在頻繁執行影響頁面外觀的代碼時(比如滾動事件監聽器),可以利用這個回調隊列進行節流。
先看一個原生實現,其中的滾動事件監聽器每次觸發都會調用名為 expensiveOperation()
(耗時操作) 的函數。當向下滾動網頁時,這個事件很快就會被觸發并執行成百上千次:
function expensiveOperation() { console.log("Invoked at", Date.now()); } window.addEventListener("scroll", () => { expensiveOperation(); });
如果想把事件處理程序的調用限制在每次重繪之前,那么就可以把它封裝到 requestAnimationFrame()
調用中:
function expensiveOperation() { console.log("Invoked at", Date.now()); } window.addEventListener("scroll", () => { window.requestAnimationFrame(expensiveOperation); });
這樣會把所有回調的執行集中在重繪鉤子,但不會過濾掉每次重繪的多余調用。我們可以定義一個標志變量,在回調中設置其狀態,就能將多余的調用屏蔽:
let enqueued = false; function expensiveOperation() { console.log("Invoked at", Date.now()); enqueued = false; } window.addEventListener("scroll", () => { if (!enqueued) { enqueued = true; window.requestAnimationFrame(expensiveOperation); } });
因為重繪是非常頻繁的操作,所以這算不上是真正的節流。更好的方法是配合使用一個計時器來限制操作執行的頻率。這樣,計時器可以限制實際的操作執行間隔,而 requestAnimationFrame()
控制在瀏覽器的哪個渲染周期中執行:
let enabled = true; function expensiveOperation() { console.log("Invoked at", Date.now()); } window.addEventListener("scroll", () => { if (enabled) { enqueued = false; window.requestAnimationFrame(expensiveOperation); window.setTimeout(() => (enabled = true), 50); } });
上面的例子將回調限制為大約 50ms 執行一次。
到此,關于“requestAnimationFrame定時動畫屏幕刷新率節流的方法”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。