前端性能優化 CSS動畫

 

最近拜讀了一下html5rocks上幾位大神寫的一篇關於CSS3動畫性能優化的文章,學到了不少,在這裏記錄一下,其中的知識都是來源於這倆篇文章,我只是截取了其中比較關注的內容出來,原文地址High Performance AnimationsAccelerated Rendering in Chrome

原理

現代瀏覽器在使用CSS3動畫時,如下四種情形繪製的效率較高,分別是: * 改變位置 * 改變大小 * 旋轉 * 改變透明度html

層?重繪?迴流和重佈局?圖層重組?

首先要了解CSS的圖層的概念(Chrome瀏覽器)html5

瀏覽器在渲染一個頁面時,會將頁面分爲不少個圖層,圖層有大有小,每一個圖層上有一個或多個節點。在渲染DOM的時候,瀏覽器所作的工做其實是: 1. 獲取DOM後分割爲多個圖層 2. 對每一個圖層的節點計算樣式結果(Recalculate style--樣式重計算) 3. 爲每一個節點生成圖形和位置(Layout--迴流和重佈局) 4. 將每一個節點繪製填充到圖層位圖中(Paint Setup和Paint--重繪) 5. 圖層做爲紋理上傳至GPU 6. 符合多個圖層到頁面上生成最終屏幕圖像(Composite Layers--圖層重組)web

Chrome中知足如下任意狀況就會建立圖層: * 3D或透視變換(perspective transform)CSS屬性 * 使用加速視頻解碼的<video>節點 * 擁有3D(WebGL)上下文或加速的2D上下文的<canvas>節點 * 混合插件(如Flash) * 對本身的opacity作CSS動畫或使用一個動畫webkit變換的元素 * 擁有加速CSS過濾器的元素 * 元素有一個包含複合層的後代節點(一個元素擁有一個子元素,該子元素在本身的層裏) * 元素有一個z-index較低且包含一個複合層的兄弟元素(換句話說就是該元素在複合層上面渲染)canvas

須要注意的是,若是圖層中某個元素須要重繪,那麼整個圖層都須要重繪。好比一個圖層包含不少節點,其中有個gif圖,gif圖的每一幀,都會重回整個圖層的其餘節點,而後生成最終的圖層位圖。因此這須要經過特殊的方式來強制gif圖屬於本身一個圖層(translateZ(0)或者translate3d(0,0,0)),CSS3的動畫也是同樣(好在絕大部分狀況瀏覽器本身會爲CSS3動畫的節點建立圖層)瀏覽器

層和CSS動畫

簡化一下上述過程,每一幀動畫瀏覽器可能須要作以下工做: 1. 計算須要被加載到節點上的樣式結果(Recalculate style--樣式重計算) 2. 爲每一個節點生成圖形和位置(Layout--迴流和重佈局) 3. 將每一個節點填充到圖層中(Paint Setup和Paint--重繪) 4. 組合圖層到頁面上(Composite Layers--圖層重組)性能優化

若是咱們須要使得動畫的性能提升,須要作的就是減小瀏覽器在動畫運行時所須要作的工做。最好的狀況是,改變的屬性僅僅印象圖層的組合,變換(transform)和透明度(opacity)就屬於這種狀況多線程

現代瀏覽器如Chrome,Firefox,Safari和Opera都對變換和透明度採用硬件加速,但IE10+不是很肯定是否硬件加速ide

觸發重佈局的屬性

有些節點,當你改變他時,會須要從新佈局(這也意味着須要從新計算其餘被影響的節點的位置和大小)。這種狀況下,被影響的DOM樹越大(可見節點),重繪所須要的時間就會越長,而渲染一幀動畫的時間也相應變長。因此須要盡力避免這些屬性佈局

一些經常使用的改變時會觸發重佈局的屬性: 盒子模型相關屬性會觸發重佈局: * width * height * padding * margin * display * border-width * border * min-height性能

定位屬性及浮動也會觸發重佈局: * top * bottom * left * right * position * float * clear

改變節點內部文字結構也會觸發重佈局: * text-align * overflow-y * font-weight * overflow * font-family * line-height * vertival-align * white-space * font-size

這麼多經常使用屬性都會觸發重佈局,能夠看到,他們的特色就是可能修改整個節點的大小或位置,因此會觸發重佈局

別使用CSS類名作狀態標記

若是在網頁中使用CSS的類來對節點作狀態標記,當這些節點的狀態標記類修改時,將會觸發節點的重繪和重佈局。因此在節點上使用CSS類來作狀態比較是代價很昂貴的

觸發重繪的屬性

修改時只觸發重繪的屬性有: * color * border-style * border-radius * visibility * text-decoration * background * background-image * background-position * background-repeat * background-size * outline-color * outline * outline-style * outline-width * box-shadow

這樣能夠看到,這些屬性都不會修改節點的大小和位置,天然不會觸發重佈局,可是節點內部的渲染效果進行了改變,因此只須要重繪就能夠了

手機就算重繪也很慢

在重繪時,這些節點會被加載到GPU中進行重繪,這對移動設備如手機的影響仍是很大的。由於CPU不如臺式機或筆記本電腦,因此繪畫巫妖的時間更長。並且CPU與GPU之間的有較大的帶寬限制,因此紋理的上傳須要必定時間

觸發圖層重組的屬性

透明度居然不會觸發重繪?

須要注意的是,上面那些觸發重繪的屬性裏面沒有opacity(透明度),很奇怪不是嗎?實際上透明度的改變後,GPU在繪畫時只是簡單的下降以前已經畫好的紋理的alpha值來達到效果,並不須要總體的重繪。不過這個前提是這個被修改opacity自己必須是一個圖層,若是圖層下還有其餘節點,GPU也會將他們透明化

強迫瀏覽器建立圖層

在Blink和WebKit的瀏覽器中,一當一個節點被設定了透明度的相關過渡效果或動畫時,瀏覽器會將其做爲一個單獨的圖層,但不少開發者使用translateZ(0)或者translate3d(0,0,0)去使瀏覽器建立圖層。這種方式能夠消除在動畫開始以前的圖層建立時間,使得動畫儘快開始(建立圖層和繪製圖層仍是比較慢的),並且不會隨着抗鋸齒而導出突變。不過這種方法須要節制,不然會由於建立過多的圖層致使崩潰

Chrome中的抗鋸齒

Chrome中,非根圖層以及透明圖層使用grayscale antialiasing而不是subpixel antialiasing,若是抗鋸齒方法變化,這個效果將會很是顯著。若是你打算預處理一個節點而不打算等到動畫開始,能夠經過這種強迫瀏覽器建立圖層的方式進行

transform變換是你的選擇

咱們經過節點的transform能夠修改節點的位置、旋轉、大小等。咱們日常會使用lefttop屬性來修改節點的位置,但正如上面所述,lefttop會觸發重佈局,修改時的代價至關大。取而代之的更好方法是使用translate,這個不會觸發重佈局

JS動畫和CSS3動畫的比較

咱們常常面臨一個抉擇:是使用JavaScript的動畫仍是使用CSS的動畫,下面將對比一下這兩種方式

JS動畫

缺點:JavaScript在瀏覽器的主線程中運行,而其中還有不少其餘須要運行的JavaScript、樣式計算、佈局、繪製等對其干擾。這也就致使了線程可能出現阻塞,從而形成丟幀的狀況。

優勢:JavaScript的動畫與CSS預先定義好的動畫不一樣,能夠在其動畫過程當中對其進行控制:開始、暫停、回放、停止、取消都是能夠作到的。並且一些動畫效果,好比視差滾動效果,只有JavaScript可以完成

CSS動畫

缺點:缺少強大的控制能力。並且很難以有意義的方式結合到一塊兒,使得動畫變得複雜且易於出問題。 優勢:瀏覽器能夠對動畫進行優化。它必要時能夠建立圖層,而後在主線程以外運行。

前瞻

Google目前正在探究經過JS的多線程(Web Workers)來提供更好的動畫效果,而不會觸發重佈局及樣式重計算

結論

動畫給予了頁面豐富的視覺體驗。咱們應該盡力避免使用會觸發重佈局和重繪的屬性,以避免失幀。最好提早申明動畫,這樣能讓瀏覽器提早對動畫進行優化。因爲GPU的參與,如今用來作動畫的最好屬性是以下幾個: * opacity * translate * rotate * scale

也許會有一些新的方式使得可使用JavaScript作出更好的沒有限制的動畫,並且不用擔憂主線程的阻塞問題。但在那以前,仍是好好考慮下如何作出流暢的動畫吧

相關文章
相關標籤/搜索