頁面絲滑程度的優化檢測

本文原創:fanjiayucss

什麼是絲滑的頁面

  • chrome團隊提出了一個以用戶爲中心的性能模型被稱爲RAIL,它爲工程師提供一個目標,只要達到目標的網頁,用戶就會以爲很流暢;它將用戶體驗拆解爲一些關鍵操做,例如:點擊,加載等;並給這些操做規定一個目標,例如:點擊一個按鈕後,多長時間給反饋用戶會以爲流暢。html

  • RAIL將影響性能的行爲劃分爲四個方面,分別是:response(響應)、animation(動畫)、idle(空閒)與load(加載)。沒錯,RAIL這個名字來自於這四個單詞的首字母,方便記憶。css3

response(響應)

  • 100ms
  • 研究代表,100ms內對用戶的輸入操做進行響應,一般會被人類認爲是當即響應。時間再長,操做與反應之間的鏈接就會中斷,人們就會以爲它的操做有延遲。
  • 例如:當用戶點擊一個按鈕,若是100ms內給出響應,那麼用戶就會以爲響應很及時,不會察覺到絲毫延遲感。

animation(動畫)

  • 60fps
  • 現現在大多數設備的屏幕刷新頻率是60hz,也就是每秒鐘屏幕刷新60次;所以網頁動畫的運行速度只要達到60fps,咱們就會以爲動畫很流暢。
  • fps指的畫面每秒鐘傳輸的幀數,**60fps指的是每秒鐘60幀;**換算下來每一幀差很少是16毫秒。
  • 但一般瀏覽器須要花費一些時間將每一幀的內容繪製到屏幕上(包括樣式計算、佈局、繪製、合成等工做),因此一般咱們只有10毫秒來執行JS代碼。

idle(空閒)

  • 爲了更好的性能,一般咱們會充分利用瀏覽器空閒週期idle period作一些低優先級的事情。例如:在空閒週期預請求一些接下來可能會用到的數據或上報分析數據等。
  • RAIL規定,空閒週期內運行的任務不得超過50ms。由於瀏覽器同一時間主線程只能處理一個任務,若是一個任務執行時間過長,瀏覽器則沒法執行其餘任務,用戶會感受到瀏覽器被卡死了,爲了達到100ms內給出響應,將空閒週期執行的任務限制爲50ms意味着,即便用戶的輸入行爲發生在空閒任務剛開始執行,瀏覽器仍有剩餘的50ms時間用來響應用戶輸入,而不會產生用戶可察覺的延遲

1.png

  • 事實上,不管是空閒任務仍是高優先級的其餘任務,執行時間都不得超過50ms。

load(加載)

  • 秒級
  • 若是不能在1秒鐘內加載網頁並讓用戶看到內容,用戶的注意力就會分散。用戶會以爲他要作的事情被打斷,若是10秒鐘還打不開網頁,用戶會感到失望,會放棄他們想作的事,之後他們或許都不會再回來。

絲滑頁面的決定因素(像素管道)

2.png

  • JavaScript:通常來講,咱們會使用JavaScript來實現一些視覺變化的效果。chrome

  • Style:計算樣式。這個過程是根據CSS選擇器,對每一個DOM元素匹配對應的CSS樣式。瀏覽器

  • Layout:佈局。具體計算每一個DOM元素最終在屏幕上顯示的大小和位置。Web頁面中元素的佈局是相對的,所以一個元素的佈局發生變化,會聯動地引起其餘元素的佈局發生變化。所以,對於瀏覽器而言,佈局過程是常常發生的。bash

  • Paint:繪製。本質上就是**填充像素的過程。**包括繪製文字、顏色、圖像、邊框和陰影等,也就是一個DOM元素全部的可視效果。通常來講,這個繪製過程是在多個層上完成的。佈局

  • Composite:渲染層合併。性能

3.png

  • 若是你修改一個DOM元素的「layout」屬性,也就是改變了元素的樣式(好比width、height或者position等),那麼瀏覽器會檢查哪些元素須要從新佈局,而後對頁面激發一個reflow(重排)過程完成從新佈局。reflow一定會引起重繪,這對於WEB的性能影響是極大的。測試

  • 影響WEB性能主要過程包括layout、paint和composite。那麼對於cssanimation而言,咱們的全部操做都是經過CSS的樣式控制動畫,只要是會觸發layout、paint和composite的CSS屬性都會直接影響動畫的性能。因此整個動畫應儘可能避開重排和重繪。字體

  • 會觸發重排重繪:調整窗口大小、改變字體、增長或者移除樣式表、內容變化、激活CSS僞類、計算offsetwidth和offsetheight等等。

  • 影響layout的CSS屬性:csstriggers.com/

使頁面絲滑的方法

####防止FSL(強制同步佈局)

  • FLS:先執行JS,而後在JS中修改了樣式從而致使樣式計算,而後樣式的改動觸發了佈局、繪製、合成。但JavaScript能夠強制瀏覽器將佈局提早執行,這就叫FSL強制同步佈局

4.png

以下代碼爲會觸發FSL的一段代碼

<body>
    <button id="btn">Click me~</button>
    <div class="container">
        <!-- 建立多個class爲box的div使效果更明顯>
        <div class="box"></div>
    </div>
    <script>
        const btn = document.querySelector('#btn');
        const container = document.querySelector('.container');
        const boxes = document.querySelectorAll('.box');

        btn.onclick = function () {
            for (var i = 0; i < boxes.length; i++) {
                boxes[i].style.width = container.offsetWidth + 'px';
            }
        }
    </script>
</body>
複製代碼

5.png

  • 面板中已經明確給出警告:Forced reflow is a likely performance bottleneck.(「強制同步佈局」可能會致使性能問題)

對代碼進行以下優化

<script>
   const btn = document.querySelector('#btn');
   const container = document.querySelector('.container');
   const boxes = document.querySelectorAll('.box');

   btn.onclick = function () {
       const newWidth = container.offsetWidth;
       for (var i = 0; i < boxes.length; i++) {
           boxes[i].style.width = newWidth + 'px';
       }
   }
</script>
複製代碼
  • 文中前半部分已經說明,計算offsetwidth會觸發重排重繪,因此第一種方法在循環中先獲取容器box的寬度,隨後設置了box元素的樣式。這會致使瀏覽器去佈局,而後計算樣式。每次更改樣式,都會致使剛剛執行的佈局失效,由於咱們又改了新的樣式,因此下一輪循環讀取寬度時,瀏覽器又要執行一次佈局,如此反覆直到循環結束。在循環期間,瀏覽器不停地執行無效佈局,因而出現了性能問題。爲了使效果更佳明顯,在CPU 6x slowdown的條件下進行了測試,對好比下:

6.png
7.png

從對比中能夠看出,前者的Rendering是後者的幾倍之多,可見,優化重排重繪,防止FSL的發生對性能的提高效果是很明顯的。

####新建圖層

  • 事實上瀏覽器在渲染頁面時,能夠將頁面分爲不少個圖層,有點相似於photoshop,一張圖片在potoshop中是由多個圖層組合而成,而瀏覽器最終顯示的頁面實際也是由多個圖層構成的。

  • 因此將本來不斷髮生變化的元素提高到單獨的圖層中,就再也不須要繪製了,瀏覽器只須要將兩個圖層合併在一塊兒便可。(注:圖層的維護也須要成本,不要一味地不斷新建圖層)

<body>    
    <div class="ball-running" style="width:100px;height:50px;background:red;position:absolute;left:0;top:0"></div>
</body>
<style>
    .ball-running { animation: run-around 4s infinite;} 

    @keyframes run-around {

        0%: { top: 0; left: 0; } 

        25% { top: 0; left: 200px; } 

        50% { top: 200px; left: 200px; } 

        75% { top: 200px; left: 0; } 

    }
</style>
複製代碼
<body>    
    <div class="ball-running" style="width:100px;height:50px;background:red;position:absolute;left:0;top:0"></div>
</body>
<style>
    .ball-running { animation: run-around 4s infinite; } 

    @keyframes run-around {

    0%: { transform: translate(0, 0); } 

    25% { transform: translate(200px, 0); } 

    50% { transform: translate(200px, 200px); } 

    75% { transform: translate(0, 200px); } 

    }
</style>
複製代碼
  • 兩者的區別是前者不斷改變元素的位置使元素運動起來,後者則經過transform來實現運動,其實是將元素動畫提高到了一個新的圖層,開啓GPU加速單獨處理圖像部分,下圖是兩者性能圖對比。

8.png
9.png

  • 能夠看到,前者不斷重複發生重排和重繪過程,然後者已經再也不發生重排和重繪,由於transform這個屬性已經由合成器單獨處理了,因此使用這個屬性能夠避免佈局與繪製。

總結

  • JS動畫要保證預留出6ms的時間給瀏覽器處理像素管道,而自身執行時間應該小於10ms來保證總體運行速度小於16ms。

  • 避免一切FSL,它很是影響性能。

  • CSS動畫儘可能使用transform屬性來完成動畫。建議使用transform的translate替代margin或position中的top、right、bottom和left,同時使用transform中的scalex或者scaley來替代width和height。

參考

相關文章
相關標籤/搜索