国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

一篇文章說清瀏覽器解析和CSS(GPU)動畫優化

zhangxiangliang / 1962人閱讀

摘要:相信不少人在做移動端動畫的時候遇到了卡頓的問題,這篇文章嘗試從瀏覽器渲染的角度一點一點告訴你動畫優化的原理及其技巧,作為你工作中優化動畫的參考。瀏覽器渲染提高動畫的優化不得不提及瀏覽器是如何渲染一個頁面。

相信不少人在做移動端動畫的時候遇到了卡頓的問題,這篇文章嘗試從瀏覽器渲染的角度;一點一點告訴你動畫優化的原理及其技巧,作為你工作中優化動畫的參考。文末有優化技巧的總結。

因為GPU合成沒有官方規范,每個瀏覽器的問題和解決方式也不同;所以文章內容僅供參考。

瀏覽器渲染

提高動畫的優化不得不提及瀏覽器是如何渲染一個頁面。在從服務器中拿到數據后,瀏覽器會先做解析三類東西:

解析html,xhtml,svg這三類文檔,形成dom樹。

解析css,產生css rule tree。

解析js,js會通過api來操作dom tree和css rule tree。

解析完成之后,瀏覽器引擎會通過dom tree和css rule tree來構建rendering tree:

rendering tree和dom tree并不完全相同,例如:或display:none的東西就不會放在渲染樹中。

css rule tree主要是完成匹配,并把css rule附加給rendering tree的每個element。

在渲染樹構建完成后,

瀏覽器會對這些元素進行定位和布局,這一步也叫做reflow或者layout。

瀏覽器繪制這些元素的樣式,顏色,背景,大小及邊框等,這一步也叫做repaint。

然后瀏覽器會將各層的信息發送給GPU,GPU會將各層合成;顯示在屏幕上。

渲染優化原理

如上所說,渲染樹構建完成后;瀏覽器要做的步驟:

reflow——》repaint——》composite

reflow和repaint

reflow和repaint都是耗費瀏覽器性能的操作,這兩者尤以reflow為甚;因為每次reflow,瀏覽器都要重新計算每個元素的形狀和位置。

由于reflow和repaint都是非常消耗性能的,我們的瀏覽器為此做了一些優化。瀏覽器會將reflow和repaint的操作積攢一批,然后做一次reflow。但是有些時候,你的代碼會強制瀏覽器做多次reflow。例如:

var content = document.getElementById("content");
content.style.width = 700px;
var contentWidth = content.offsetWidth;
content.style.backgound = "red";

以上第三行代碼,需要瀏覽器reflow后;再獲取值,所以會導致瀏覽器多做一次reflow。

下面是一些針對reflow和repaint的最佳實踐:

不要一條一條地修改dom的樣式,盡量使用className一次修改。

將dom離線后修改

使用documentFragment對象在內存里操作dom。

先把dom節點display:none;(會觸發一次reflow)。然后做大量的修改后,再把它顯示出來。

clone一個dom節點在內存里,修改之后;與在線的節點相替換。

不要使用table布局,一個小改動會造成整個table的重新布局。

transform和opacity只會引起合成,不會引起布局和重繪。

從上述的最佳實踐中你可能發現,動畫優化一般都是盡可能地減少reflow、repaint的發生。關于哪些屬性會引起reflow、repaint及composite,你可以在這個網站找到https://csstriggers.com/。

composite

在reflow和repaint之后,瀏覽器會將多個復合層傳入GPU;進行合成工作,那么合成是如何工作的呢?

假設我們的頁面中有A和B兩個元素,它們有absolute和z-index屬性;瀏覽器會重繪它們,然后將圖像發送給GPU;然后GPU將會把多個圖像合成展示在屏幕上。


A
B

我們將A元素使用left屬性,做一個移動動畫:


A
B

在這個例子中,對于動畫的每一幀;瀏覽器會計算元素的幾何形狀,渲染新狀態的圖像;并把它們發送給GPU。(你沒看錯,position也會引起瀏覽器重排的)盡管瀏覽器做了優化,在repaint時,只會repaint部分區域;但是我們的動畫仍然不夠流暢。

因為重排和重繪發生在動畫的每一幀,一個有效避免reflow和repaint的方式是我們僅僅畫兩個圖像;一個是a元素,一個是b元素及整個頁面;我們將這兩張圖片發送給GPU,然后動畫發生的時候;只做兩張圖片相對對方的平移。也就是說,僅僅合成緩存的圖片將會很快;這也是GPU的優勢——它能非常快地以亞像素精度地合成圖片,并給動畫帶來平滑的曲線。

為了僅發生composite,我們做動畫的css property必須滿足以下三個條件:

不影響文檔流。

不依賴文檔流。

不會造成重繪。

滿足以上以上條件的css property只有transform和opacity。你可能以為position也滿足以上條件,但事實不是這樣,舉個例子left屬性可以使用百分比的值,依賴于它的offset parent。還有em、vh等其他單位也依賴于他們的環境。

我們使用translate來代替left


A
B

瀏覽器在動畫執行之前就知道動畫如何開始和結束,因為瀏覽器沒有看到需要reflow和repaint的操作;瀏覽器就會畫兩張圖像作為復合層,并將它們傳入GPU。

這樣做有兩個優勢:

動畫將會非常流暢

動畫不在綁定到CPU,即使js執行大量的工作;動畫依然流暢。

看起來性能問題好像已經解決了?在下文你會看到GPU動畫的一些問題。

GPU是如何合成圖像的

GPU實際上可以看作一個獨立的計算機,它有自己的處理器和存儲器及數據處理模型。當瀏覽器向GPU發送消息的時候,就像向一個外部設備發送消息。

你可以把瀏覽器向GPU發送數據的過程,與使用ajax向服務器發送消息非常類似。想一下,你用ajax向服務器發送數據,服務器是不會直接接受瀏覽器的存儲的信息的。你需要收集頁面上的數據,把它們放進一個載體里面(例如JSON),然后發送數據到遠程服務器。

同樣的,瀏覽器向GPU發送數據也需要先創建一個載體;只不過GPU距離CPU很近,不會像遠程服務器那樣可能幾千里那么遠。但是對于遠程服務器,2秒的延遲是可以接受的;但是對于GPU,幾毫秒的延遲都會造成動畫的卡頓。

瀏覽器向GPU發送的數據載體是什么樣?這里給出一個簡單的制作載體,并把它們發送到GPU的過程。

畫每個復合層的圖像

準備圖層的數據

準備動畫的著色器(如果需要)

向GPU發送數據

所以你可以看到,每次當你添加transform:translateZ(0)will-change:transform給一個元素,你都會做同樣的工作。重繪是非常消耗性能的,在這里它尤其緩慢。在大多數情況,瀏覽器不能增量重繪。它不得不重繪先前被復合層覆蓋的區域。

隱式合成

還記得剛才a元素和b元素動畫的例子嗎?現在我們將b元素做動畫,a元素靜止不動。

和剛才的例子不同,現在b元素將擁有一個獨立復合層;然后它們將被GPU合成。但是因為a元素要在b元素的上面(因為a元素的z-index比b元素高),那么瀏覽器會做什么?瀏覽器會將a元素也多帶帶做一個復合層!

所以我們現在有三個復合層a元素所在的復合層、b元素所在的復合層、其他內容及背景層。

一個或多個沒有自己復合層的元素要出現在有復合層元素的上方,它就會擁有自己的復合層;這種情況被稱為隱式合成。

瀏覽器將a元素提升為一個復合層有很多種原因,下面列舉了一些:

3d或透視變換css屬性,例如translate3d,translateZ等等(js一般通過這種方式,使元素獲得復合層)