高性能Web動畫和渲染原理系列(5)合成層的生成條件和陷阱

示例代碼託管在:http://www.github.com/dashnowords/blogs前端

博客園地址:《大史住在大前端》原創博文目錄git

華爲雲社區地址:【你要的前端打怪升級指南】github

一. 硬件加速相關的幾個概念

以前介紹到了RenderLayer渲染層的概念,在涉及到硬件加速的話題時,出現了不少新的概念,參考《Webkit技術內幕》一書的介紹總結以下:後端

Webkit決定將哪些RenderLayer對象組合在一塊兒,造成一個有後端存儲的新層,這一新層不久後會用於合成,這裏稱之爲合成層CompositingLayer)。每個合成層都會對應一個或多個後端存儲,由RenderLayerBacking類進行統一管理,後端存儲空間使用GraphicsLayer來表示,也就是說RenderLayerBacking管理着一個或多個與對應的合成層有關的GraphicsLayer瀏覽器

筆者旁白:對於渲染過程來講,只須要理解這裏造成了新的CompositingLayer合成層就能夠了,其餘的層概念基本都是用於實現對CompositingLayer功能支持的,概念數量太多對於理解宏觀流程是一大障礙。ide

二. 合成層的生成條件

顯式提高

合成層的處理是依賴於硬件加速的,可是GPU的存儲空間有限最好不要濫用,過多的合成層有可能還會形成相反的效果,因此瀏覽器只會將知足下列任意條件的RenderLayer提高爲CompositingLayer佈局

  • 具備CSS3D屬性或CSS透視效果
  • 包含的RenderObject節點表示的是使用硬件加速的視頻解碼技術的HTML5video元素
  • 包含的RenderObject節點包含使用了硬件加速的Canvas2DWebGL技術
  • 使用了CSS透明效果或CSS變形動畫
  • 使用了硬件加速的CSS Filters技術(有的文獻中表示filters屬性並無提高爲合成層的效果,推測只有一部分filters濾鏡效果須要使用硬件加速,並不是全部)
  • 使用了剪裁Clip或者反射Reflection,而且它的後代中包含一個合成層
  • 擁有一個Z座標比本身小的兄弟節點,且該節點是一個合成層。

上面的規則裏咱們最熟悉的可能就是transform:translateZ(0)或者在關鍵幀動畫的定義中改變transformopacity屬性。固然,隨着技術的演進,上面的規則並不必定全面Chromium官網提供的開發者演講PPT中也對提高的理由進行了相關的描述:測試

你能夠在Chrome調試面板的【Layers】功能中對分層相關的結果進行檢視,查看哪些層進行了提高以及被提高的具體緣由,避免出現與本身意圖相悖的層提高:動畫

隱式提高

RenderLayer知足特殊條件時被提高爲CompositingLayer對開發者而言是比較可控的。但除此以外,在瀏覽器的合成階段,還存在隱式合成的情況,一些特定的場景中出現的合成層並非開發者主觀指望的。

隱式合成主要發生在元素出現重疊時,層級較低的元素若是被提高爲合成層後,最終合成的結果就可能出如今原來比本身層級更高的元素之上,從而出現錯誤的堆疊關係,爲了糾正這種關係,只能讓本來層級高(可是並不用提高爲合成層的元素)發生提高也成爲合成層。例以下面的代碼:

<div style="position:absolute;height:200px;width:200px;background-color: #DA5961;"></div>
<div style="position:absolute;left:30px;top:50px;height:200px;width:200px;background-color: #3498db;"></div>
<div style="position:absolute;left:60px;top:100px;height:200px;width:200px;background-color: #1abc9c;"></div>

三個div盒子堆疊在一塊兒,能夠看到它們都繪製在同一個層上(這裏的層並不與RenderLayer對應,畢竟它只是一箇中間態的樹結構):

此時若是爲最底下的紅色矩形添加transform:translateZ(0)屬性將其提高爲合成層後,爲了保證正確的堆疊關係,藍色和綠色的矩形就會被提高爲合成層,代碼以下:

<div style="transform:translateZ(0);position:absolute;height:200px;width:200px;background-color: #DA5961;"></div>
<div style="position:absolute;left:30px;top:50px;height:200px;width:200px;background-color: #3498db;"></div>
<div style="position:absolute;left:60px;top:100px;height:200px;width:200px;background-color: #1abc9c;"></div>

藍色和綠色的矩形並無造成獨立的合成層,而是被壓縮在同一個合成層中:

從上圖中的細節信息中能夠看到,提高的緣由是layerFotSquashingContent,也就是爲了保證堆疊順序的正確,用一個單獨的合成層來將受到影響的元素收集在一塊兒,既保證堆疊順序,也避免在指望以外生成過多的合成層。若是調整綠色矩形的位置,就能夠看到,當視覺上不存在覆蓋時,它就不須要提高了:

BUT!!!還沒完,最坑的部分來了,若是此時給藍色的div加上一點動畫,你會發現綠色div又被提高到了獨立的合成層上,儘管他們之間並無重疊區,但仍是被提高了:

從圖中的合成緣由能夠看到:它可能和一個相鄰的合成層元素髮生交疊,因此被提高了。沒錯,就是「可能」。Fouber這篇CSS硬件加速也有坑中的示例更加詳細,子元素引起父元素提高,父元素又引起兄弟元素提高。

三. 硬件加速的權衡

全部的技術方案都是有代價的,這是亙古不變的道理,合成層的好處很明顯,GPUCPU的處理速度快不少,觸發repaint重繪時,只須要重繪獨立的層,而後從新合成便可,不須要重繪整個畫面。但它也存在一些弊端:首先是數據傳輸的問題,CPUGPU的關係就比如客戶端和服務端同樣,它們的協做是須要傳輸數據的,當層的數量達到必定量級後,傳輸的速度就會影響到總體的處理效率,進而致使在一些低中端設備上出現閃爍等現象;另外,每一個合成層都具會佔據額外的內存,這個數量一般比開發者覺得的要大的多,尤爲是在移動端這種硬件資源受限制的場景中,過量的內存使用分分鐘就會讓應用崩潰。

四. 動畫實現的一些建議

  1. 使用transform實現動畫

    這多是咱們編寫動畫時聽到最多的建議了。例如使用lefttop來實現位置動畫時,絕對定位的元素會造成RenderLayer,可是卻不符合提高爲CompositingLayer的條件,因此動畫元素就會和Document處在同一個合成層裏,持續進行的動畫就會致使Document這一層(一般是正常文檔流這一層,包含了大量的流式佈局的元素)不斷重繪,從而影響渲染效率,若是可以讓動畫的節點放到單獨的合成層裏,就能夠避免這種大規模重繪,並藉助GPU加速合成的能力加速整個渲染流程。

  2. 排查被動提高的情形

    被動提高主要是指「兄弟元素相對層級低於本身但倒是一個合成層」的情形以及「發生堆疊遮擋的幾個元素中層級較低的元素被提高爲合成層」的情況。通常的解決方案是主動提高動畫元素的z-index值或者調整文檔結構中節點的前後順序,固然全部的結果都還須要經過測試來確認。

  3. 考慮合成層的空間佔用

    合成層的後端存儲是渲染後的像素點數據,它的體積可能會很是大,在使用大屏圖片時須要儘量將其壓縮至視覺可接受的範圍而不能一味追求高清,對於純色的元素,可使用較小的尺寸並藉助transform:scale來放大至須要的尺寸。

  4. 實測爲王

    任何方案都只是一種思路,必須經過在真實環境測試驗證才能確認其有效性。

相關文章
相關標籤/搜索