你可能已將在某些專案中使用過CSS Animations 或 CSS Transitions。(如果還沒有,可以去CSS-Trick's檢視一些和animations、transitions相關的文章。)你也許注意到,在你寫的一些動畫效果中,有些執行的很流暢,有些則顯得比較卡頓。你是否知道這其中的原因呢?
在這篇文章中,我們將會去探究一下瀏覽器是如何去處理CSS Animations和CSS Transitions的,以便使你在寫一些動畫效果之前就可以對該動畫在瀏覽器中執行效果有一個心理預判。有了這些預判,你就可以設計出一些在瀏覽器中執行流暢的動畫效果,從而帶來更流暢的使用者體驗。
瀏覽器的內部工作讓我們瞭解一些瀏覽器的工作原理,一探究竟。一旦我們瞭解了瀏覽器是如何工作的,我們就可以更好的去駕馭它。
現代瀏覽器通常擁有兩個重要的執行執行緒,這兩個執行緒相互配合來渲染出頁面:
- 主執行緒
- 排版執行緒
- 執行JavaScript
- 計算HTML元素的CSS樣式
- 佈局頁面
- 把頁面元素繪製成一個或多個點陣圖
- 把這些點陣圖移交給排版執行緒
- 通過GPU渲染點陣圖,並顯示在螢幕上
- 向主執行緒請求更新點陣圖的可見部分或即將可見的部分
- 判斷出當前頁面處於可見的部分
- 判斷出即將通過頁面滾動而可見的部分
- 隨著使用者滾動頁面來移動這些部分(譯者注:可見部分的和即將可見的部分)
在另一方面,排版執行緒對使用者輸入保持著非常快的響應。當頁面變化時,排版執行緒嘗試以每秒60幀的速度去重繪頁面,即便這時頁面還不完整。
舉例來說,當使用者滾動頁面時,排版執行緒向主執行緒請求更新頁面新顯示部分的點陣圖,但是,如果此時主執行緒並不能迅速響應請求,排版執行緒並不會去等待響應,它會用它目前所擁有的這部分頁面的內容去渲染頁面,由於對應的內容還沒有,所以會以白板的形式渲染出來。
GPU
我前邊提到過排版執行緒通過GPU把點陣圖繪製到了螢幕上。讓我們快速的過一下GPU相關的東西。
GPU是一種晶片,在今天的大多數手機,平板以及電腦中都能發現它的身影。它是非常專業的,這意味著GPU在某些方面非常擅長,但是在另外一些方面去表現不好。
GPU比較擅長於:- 繪製點陣圖到螢幕
- 重複的繪製同一個點陣圖
- 在不同的位置,以不同的旋轉角度,或者不同的縮放大小來繪製同一個點陣圖。
GPU相對慢的地方:
- 將點陣圖載入到視訊記憶體裡。
transition: height
現在我們已經在軟體層面和硬體層面對如何渲染頁面有了一個粗略的認識。接下來,讓我們看一下瀏覽器的主執行緒和排版執行緒是如何協同工作來完成一個CSS Transition的。
假設我們想要將一個元素的高度值從100px轉換到200px,如下所示:
div { height: 100px; transition: height 1s linear; } div:hover { height: 200px; }主執行緒和排版執行緒會根據下圖所示時序圖來完成這個Transition。注意:在橙色方框中的操作是潛在的耗時操作,藍色方框中的操作是較快的操作。
正如你所見,整個過程有很多橙色的方框,意味著瀏覽器有相當繁重的工作要處理,也意味著這個Transition可能會出現卡頓。
在整個Transition的每一幀中,瀏覽器都要去重新佈局,繪製頁面,並把最新的點陣圖物件載入到GPU。我們前邊瞭解過,把點陣圖物件載入到GPU的記憶體中是個相對緩慢的操作。
瀏覽器之所以要在每一幀動畫上處理如此繁重的工作是因為這個元素的內容一直在變化。修改一個元素的高度可能會引起其子元素也要相應的改變大小,因此瀏覽器必須去重新佈局。重新佈局後,主執行緒必須為該元素重新生成點陣圖物件。
transition: transform
由此可見,對高度進行的Transition相對來說效能比較差,那有一些效能比較好的Transition嗎?
假設我們想要把一個元素從一半大小縮放到實際大小,並假設我們使用CSS的transform 屬性來對它進行縮放,同時使用CSS的transition屬性來生成縮放的動畫效果,如下所示:
div { transform: scale(0.5); transition: transform 1s linear; } div:hover { transform: scale(1.0); }讓我們看下一下這個例子的時序圖:
我們看到只有很少的幾個橙色的方框,意味著這個動畫效果可能會很流暢!那麼,一個元素的transform動畫效果與其高度的動畫效果有什麼不同呢?
根據定義,CSS的transform屬性不會改變元素的佈局,也不會影響到其周圍的元素。它把元素當做一個整體看待——縮放整個元素、旋轉整個元素或者移動整個元素。
這對瀏覽器來說是一個好訊息!瀏覽器只需在動畫開始的時候生成這個元素的點陣圖物件,並把它傳遞給GPU。在這之後,瀏覽器無需再做任何重新佈局,繪製頁面以及傳遞點陣圖物件的操作了,相反,瀏覽器可以利用GPU擅長的繪製的特點來快速的在不同的位置,旋轉或縮放同一個點陣圖物件。
設計決策
那麼,是否這就意味這我們不要去緩動一個元素的高度?非也,一些情況下,這是你的設計效果的一部分,並且動畫效果可以非常快的完成。也許動畫的元素是孤立的,不會引起頁面其他部分進行重新佈局;也許該元素只是單純的進行重繪,瀏覽器可以快速的完成;也許該元素很小,瀏覽器只需將很小的點陣圖物件傳遞給GPU。
當然了,在不影響你設計的視覺效果的情況下,最好去緩動一個效能較好的CSS屬性,如transform,而不是去緩動一個效能較差的CSS屬性,如height。舉例來說,假設你的設計中有一個按鈕,當點選它的時候會出來一個選單,試著去緩動選單的transform屬性來顯示它而不是緩動它的top或height屬性來達到類似的效果。
在動畫上特別快的CSS屬性包括:
- CSS transform
- CSS opacity
- CSS filter (依賴於過濾器的複雜度和瀏覽器)
本文轉載自:OSCHINA