requestanimationframe_requestAnimationFrame详解以及无线页面优化
推薦:10個我必備的 VS code 拓展
requestAnimationFrame是瀏覽器用于定時循環操作的一個接口,類似于setTimeout,主要用途是按幀對網頁進行重繪。
設置這個API的目的是為了讓各種網頁動畫效果(DOM動畫、Canvas動畫、SVG動畫、WebGL動畫)能夠有一個統一的刷新機制,從而節省系統資源,提高系統性能,改善視覺效果。代碼中使用這個API,就是告訴瀏覽器希望執行一個動畫,讓瀏覽器在下一個動畫幀安排一次網頁重繪。
requestAnimationFrame的優勢,在于充分利用顯示器的刷新機制,比較節省系統資源。顯示器有固定的刷新頻率(60Hz或75Hz),也就是說,每秒最多只能重繪60次或75次,requestAnimationFrame的基本思想就是與這個刷新頻率保持同步,利用這個刷新頻率進行頁面重繪。此外,使用這個API,一旦頁面不處于瀏覽器的當前標簽,就會自動停止刷新。這就節省了CPU、GPU和電力。
不過有一點需要注意,requestAnimationFrame是在主線程上完成。這意味著,如果主線程非常繁忙,requestAnimationFrame的動畫效果會大打折扣。
requestAnimationFrame使用一個回調函數作為參數。這個回調函數會在瀏覽器重繪之前調用。
requestID = window.requestAnimationFrame(callback);目前,主要瀏覽器Firefox 23 / IE 10 / Chrome / Safari)都支持這個方法。可以用下面的方法,檢查瀏覽器是否支持這個API。如果不支持,則自行模擬部署該方法。
window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })();上面的代碼按照1秒鐘60次(大約每16.7毫秒一次),來模擬requestAnimationFrame。
使用requestAnimationFrame的時候,只需反復調用它即可。
function repeatOften() { // Do whatever requestAnimationFrame(repeatOften);}requestAnimationFrame(repeatOften);cancelAnimationFrame方法
cancelAnimationFrame方法用于取消重繪。
window.cancelAnimationFrame(requestID);它的參數是requestAnimationFrame返回的一個代表任務ID的整數值。
var globalID;function repeatOften() { $(").appendTo("body"); globalID = requestAnimationFrame(repeatOften);}$("#start").on("click", function() { globalID = requestAnimationFrame(repeatOften);});$("#stop").on("click", function() { cancelAnimationFrame(globalID);});上面代碼持續在body元素下添加div元素,直到用戶點擊stop按鈕為止。
實例
下面,舉一個實例。
假定網頁中有一個動畫區塊。
<div id="anim">點擊運行動畫div>然后,定義動畫效果。
var elem = document.getElementById("anim");var startTime = undefined;function render(time) { if (time === undefined) time = Date.now(); if (startTime === undefined) startTime = time; elem.style.left = ((time - startTime)/10 % 500) + "px";}最后,定義click事件。
elem.onclick = function() { (function animloop(){ render(); requestAnimFrame(animloop); })();};運行效果可查看jsfiddle。
無線頁面動畫優化實例
無線頁面本就分秒必爭,更不用說當我們在無線頁面中使用動畫的時候。不管是css動畫還是canvas動畫,我們都需要時刻小心著,并且有必要掌握頁面性能的基本分析方法。
既然我們的目標是優化,那么就與瀏覽器的一些渲染和執行機制有關,更好的迎合瀏覽器的行為方式,才可以讓我們的動畫流暢而優美。
沒錯,瀏覽器是老大,全聽它的。
一、設備刷新率(幀率)
我們想讓頁面變快,想讓動畫流暢,我們需要先了解一下是什么在影響著我們的感知。
頁面運行在設備的瀏覽器中,現在市面上的移動設備的刷新頻率大多是60次/秒(幀率)。所以給瀏覽器渲染每一幀的畫面的時間應該是(1s/60=16.67ms)。
但實際上,瀏覽器并不是把功夫全花在為我們渲染頁面上,他還需要做一些額外的工作,比如渲染隊列的管理和不同線程的切換等等。所以,單純的瀏覽器渲染工作留給我們的時間大約也就是10ms左右,當我們在每一幀所做的渲染操作大于這個時間的時候,比較直觀的表現就是頁面卡頓,動畫卡頓。
當我們使用css animation完成動畫時,這一點看起來沒有那么重要,因為瀏覽器會為我們handle一些事情。但是當我們需要使用js比如canvas來實現流暢的逐幀動畫時,需要牢記這個有限的時間,它很重要。
二、瀏覽器的頁面渲染流水線
我們的代碼是如何一步步的渲染成頁面的呢?
- **JavaScript。一般來說,我們使用JavaScript來實現一些頁面邏輯,但偶爾我們也可能會使用JavaScript來實現一些視覺變化的效果。比如用jQuery的animate函數做一個動畫、或者往頁面里添加一些DOM元素等。當然,現在更可能的是使用CSS Animations, Transitions和web Animation API。
- 計算樣式(Style)。這個過程是通過樣式文件中的CSS選擇器,對每個DOM元素匹配對應的CSS樣式。
- 布局(Layout)。上一步確定了每個DOM元素的樣式規則,這一步就是具體計算每個DOM元素最終在屏幕上顯示的大小和位置。web頁面中元素的布局是相對的,因此一個元素的布局發生變化,會聯動地引發其他元素的布局發生變化。因此對于瀏覽器來說,布局過程是經常發生的。
- 繪制(Paint)。繪制,本質上就是填充像素的過程。包括繪制文字、顏色、圖像、邊框和陰影等,也就是一個DOM元素所有的可視效果。一般來說,這個繪制過程是在多個層上完成的。
- 渲染層合并(Composite)。由上一步可知,對頁面中DOM元素的繪制是在多個層上進行的。在每個層上完成繪制過程之后,瀏覽器會將所有層按照合理的順序合并成一個圖層,然后顯示在屏幕上。對于有位置重疊的元素的頁面,這個過程尤其重要,因為一旦圖層的合并順序出錯,將會導致元素顯示異常。
看起來每個頁面都會經歷這樣的幾個過程,然而我們其實可以使用一些技巧,幫助瀏覽器跳過某些步驟,而縮短他的工作時間。
1.五個步驟都消耗了時間
當我們在js中改變了某個DOM元素的layout時,那么瀏覽器就會檢查頁面中的哪些元素需要重新布局,然后對頁面激發一個reflow過程以完成頁面的重新布局。被reflow的元素,接下來就一定會再次經過Paint和Composite這兩個過程,以渲染出最新的頁面。
2.跳過layout這一步
當我們只修改了一個DOM元素的paint only屬性的時候,比如background-image/color/box-shadow等。這個時候不會觸發layout,瀏覽器在完成樣式的計算之后就會跳過layout的過程,就只Paint和Composite了。
3.跳過layout和paint這兩步
如果你修改一個非樣式且非繪制的CSS屬性,那么瀏覽器會在完成樣式計算之后,跳過布局和繪制的過程,直接Composite。
我們嘗試下使用transform動畫來盡可能的達到這種效果。
三、使用transform實現動畫
我們可能經常需要做一些動畫,比如在做某些揭秘或者新手引導的效果時,會需要做一些將內容移入移出的操作。
當然可能第一個想到的就是 css transition 只要過渡一下 left 值或者 bottom 的值就可以了。效果或許很快就會實現,但是當我們在一個頁面頻繁的做著這樣的移入移出操作時,細心地我們放在手機中(6P)看一看,動畫并不會很流暢,尤其是在某些低端機型上。
我們換用 transform 來實現相同的效果:
transition: left 2s ease-in-out; ---> transition: transform 2s ease-in-out;left: xxx; ---> transform: translate3d(xxx, yyy, zzz);原因在于:
- 簡單的說頁面的繪制并不是在單層的畫面里完成的,這其中有渲染層合成層等概念。對 opacity 和 transform 應用了 CSS 動畫的渲染層、有 3D 或者 perspective transform 的 CSS 屬性的渲染層等滿足一些條件的渲染層被稱為合成層;
- 合成層有自己的渲染上下文,并且交由 GPU 處理,比 CPU 要快;
- 當頁面需要重繪時,合成層的元素只會重繪自己層內的元素,而非整個頁面;
優化過后再放在設備里查看,可以感受到效果明顯的提升。其實這里就做到了上面提到的,節省了layout和paint。
四、從css到canvas,使用requestAnimationFrame
現在css的動畫越來越好用,也能滿足越來越多的需求。但在某些復雜的需求中我們可能還是要求助于js。
比如說我這里實現的一個半圓的動畫:[半圓progress] [Source Code]。看起來使用css動畫就完全可以滿足我的需求,但是當需求變化的時候,我們也只能擁抱變化了。
使用requestAnimationFrame
[圓弧progress][Source Code] 這里用canvas實現了自定義弧度圓弧的增長動畫。
這里我們借助這個動畫效果看一下是如何使用canvas和requestAnimationFrame來實現流暢的逐幀動畫的。
window.requestAnimationFrame 是一個專門為動畫而生的 web API 。它通知瀏覽器在頁面重繪前執行你的回調函數。通常來說被調用的頻率是每秒60次。
假設我們的頁面上有一個動畫效果,如果我們想保證每一幀的順利繪制,那么我們就需要requestAnimationFrame來保證我們的繪制時機了。
很多框架和示例代碼都是用setTimeout或setInterval來實現頁面中的動畫效果,比如jQuery中的animation。這種實現方式的問題是,你在setTimeout或setInterval中指定的回調函數的執行時機是無法保證的。它將在這一幀動畫的_某個時間點_被執行,很可能是在幀結束的時候。這就意味這我們可能失去這一幀的信息。
requestAnimationFrame的其他高能用法
根據requestAnimationFrame的特性,其實我們還可以在很多別的想不到的地方來一顯身手。
- 動畫:也是它的主要用途,它將我們動畫的執行時機和執行頻率交由瀏覽器決定,以得到更好的性能;
- 函數節流:requestAnimationFrame 的執行頻率(一幀)是16.67ms,利用這一個特征就可以做到函數節流,避免高頻事件在一幀內做多余的無用功的函數執行,例:
分幀初始化:同樣利用一幀的執行時間將模塊的初始化或渲染函數分散到不同的幀中來執行,這樣每個模塊都有16.67ms的執行時間,而不是一股腦的堆在那里等著執行;
var rAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || function(c) { setTimeout(c, 1 / 60 * 1000); }; function render() { self.$container.html(itemHtml); self.$container.find('.J_LazyLoad').lazyload(); } rAF(render);五、分析你的無線頁面
我們還是借助這個例子,[圓弧progress][Source Code] 簡單的看下如何分析無線頁面的性能。
這里的實現思路是這樣的:
1 - 確定圓弧的起始弧度(0.75PI)和終止弧度(根據當前分值占上限分值的比例計算,最大為2.25PI);2 - 隨著時間的增長逐幀繪制終點位置 requestAnimationFrame(draw);3 - 根據每一幀的終點位置的 cos 和 sin 值得到跟隨的圓圈坐標并繪制;
但當然,實現完成只是走了第一步,我們來借助Chrome Timeline來分析一下這個簡單的頁面。
使用Timeline就可以看到頁面的幾種指標,幀率,js執行等等。就可以針對出現問題的幀下手優化。
在分析頁面性能的時候,嚴重推薦閱讀:[https://developer.chrome.com/devtools/docs/timeline] .timeline的詳細使用說明,它真的很強大,能幫助我們分析到頁面的各個方面的問題。
參考:
http://javascript.ruanyifeng.com/htmlapi/requestanimationframe.html
http://www.cnblogs.com/skylar/p/5374916.html
推薦閱讀:
扶我起來,我還能學:迎接 Vue 3.0
CSS 中calc()的完整指南(一)
Vue 3中令人激動的新功能:Composition API
掘金小冊全網8折優惠入口
公眾號后臺回復【前端】加入群
好文和朋友一起看~總結
以上是生活随笔為你收集整理的requestanimationframe_requestAnimationFrame详解以及无线页面优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: druid 多数据源_Druid连接池的
- 下一篇: python sns绘制回归线_SVM-