成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

一篇文章說(shuō)清瀏覽器解析和CSS(GPU)動(dòng)畫優(yōu)化

zhangxiangliang / 1963人閱讀

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

相信不少人在做移動(dòng)端動(dòng)畫的時(shí)候遇到了卡頓的問(wèn)題,這篇文章嘗試從瀏覽器渲染的角度;一點(diǎn)一點(diǎn)告訴你動(dòng)畫優(yōu)化的原理及其技巧,作為你工作中優(yōu)化動(dòng)畫的參考。文末有優(yōu)化技巧的總結(jié)。

因?yàn)镚PU合成沒有官方規(guī)范,每個(gè)瀏覽器的問(wèn)題和解決方式也不同;所以文章內(nèi)容僅供參考。

瀏覽器渲染

提高動(dòng)畫的優(yōu)化不得不提及瀏覽器是如何渲染一個(gè)頁(yè)面。在從服務(wù)器中拿到數(shù)據(jù)后,瀏覽器會(huì)先做解析三類東西:

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

解析css,產(chǎn)生css rule tree。

解析js,js會(huì)通過(guò)api來(lái)操作dom tree和css rule tree。

解析完成之后,瀏覽器引擎會(huì)通過(guò)dom tree和css rule tree來(lái)構(gòu)建rendering tree:

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

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

在渲染樹構(gòu)建完成后,

瀏覽器會(huì)對(duì)這些元素進(jìn)行定位和布局,這一步也叫做reflow或者layout。

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

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

渲染優(yōu)化原理

如上所說(shuō),渲染樹構(gòu)建完成后;瀏覽器要做的步驟:

reflow——》repaint——》composite

reflow和repaint

reflow和repaint都是耗費(fèi)瀏覽器性能的操作,這兩者尤以reflow為甚;因?yàn)槊看蝦eflow,瀏覽器都要重新計(jì)算每個(gè)元素的形狀和位置。

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

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

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

下面是一些針對(duì)reflow和repaint的最佳實(shí)踐:

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

將dom離線后修改

使用documentFragment對(duì)象在內(nèi)存里操作dom。

先把dom節(jié)點(diǎn)display:none;(會(huì)觸發(fā)一次reflow)。然后做大量的修改后,再把它顯示出來(lái)。

clone一個(gè)dom節(jié)點(diǎn)在內(nèi)存里,修改之后;與在線的節(jié)點(diǎn)相替換。

不要使用table布局,一個(gè)小改動(dòng)會(huì)造成整個(gè)table的重新布局。

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

從上述的最佳實(shí)踐中你可能發(fā)現(xiàn),動(dòng)畫優(yōu)化一般都是盡可能地減少reflow、repaint的發(fā)生。關(guān)于哪些屬性會(huì)引起reflow、repaint及composite,你可以在這個(gè)網(wǎng)站找到https://csstriggers.com/。

composite

在reflow和repaint之后,瀏覽器會(huì)將多個(gè)復(fù)合層傳入GPU;進(jìn)行合成工作,那么合成是如何工作的呢?

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


A
B

我們將A元素使用left屬性,做一個(gè)移動(dòng)動(dòng)畫:


A
B

在這個(gè)例子中,對(duì)于動(dòng)畫的每一幀;瀏覽器會(huì)計(jì)算元素的幾何形狀,渲染新狀態(tài)的圖像;并把它們發(fā)送給GPU。(你沒看錯(cuò),position也會(huì)引起瀏覽器重排的)盡管瀏覽器做了優(yōu)化,在repaint時(shí),只會(huì)repaint部分區(qū)域;但是我們的動(dòng)畫仍然不夠流暢。

因?yàn)橹嘏藕椭乩L發(fā)生在動(dòng)畫的每一幀,一個(gè)有效避免reflow和repaint的方式是我們僅僅畫兩個(gè)圖像;一個(gè)是a元素,一個(gè)是b元素及整個(gè)頁(yè)面;我們將這兩張圖片發(fā)送給GPU,然后動(dòng)畫發(fā)生的時(shí)候;只做兩張圖片相對(duì)對(duì)方的平移。也就是說(shuō),僅僅合成緩存的圖片將會(huì)很快;這也是GPU的優(yōu)勢(shì)——它能非??斓匾詠喯袼鼐鹊睾铣蓤D片,并給動(dòng)畫帶來(lái)平滑的曲線。

為了僅發(fā)生composite,我們做動(dòng)畫的css property必須滿足以下三個(gè)條件:

不影響文檔流。

不依賴文檔流。

不會(huì)造成重繪。

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

我們使用translate來(lái)代替left


A
B

瀏覽器在動(dòng)畫執(zhí)行之前就知道動(dòng)畫如何開始和結(jié)束,因?yàn)闉g覽器沒有看到需要reflow和repaint的操作;瀏覽器就會(huì)畫兩張圖像作為復(fù)合層,并將它們傳入GPU。

這樣做有兩個(gè)優(yōu)勢(shì):

動(dòng)畫將會(huì)非常流暢

動(dòng)畫不在綁定到CPU,即使js執(zhí)行大量的工作;動(dòng)畫依然流暢。

看起來(lái)性能問(wèn)題好像已經(jīng)解決了?在下文你會(huì)看到GPU動(dòng)畫的一些問(wèn)題。

GPU是如何合成圖像的

GPU實(shí)際上可以看作一個(gè)獨(dú)立的計(jì)算機(jī),它有自己的處理器和存儲(chǔ)器及數(shù)據(jù)處理模型。當(dāng)瀏覽器向GPU發(fā)送消息的時(shí)候,就像向一個(gè)外部設(shè)備發(fā)送消息。

你可以把瀏覽器向GPU發(fā)送數(shù)據(jù)的過(guò)程,與使用ajax向服務(wù)器發(fā)送消息非常類似。想一下,你用ajax向服務(wù)器發(fā)送數(shù)據(jù),服務(wù)器是不會(huì)直接接受瀏覽器的存儲(chǔ)的信息的。你需要收集頁(yè)面上的數(shù)據(jù),把它們放進(jìn)一個(gè)載體里面(例如JSON),然后發(fā)送數(shù)據(jù)到遠(yuǎn)程服務(wù)器。

同樣的,瀏覽器向GPU發(fā)送數(shù)據(jù)也需要先創(chuàng)建一個(gè)載體;只不過(guò)GPU距離CPU很近,不會(huì)像遠(yuǎn)程服務(wù)器那樣可能幾千里那么遠(yuǎn)。但是對(duì)于遠(yuǎn)程服務(wù)器,2秒的延遲是可以接受的;但是對(duì)于GPU,幾毫秒的延遲都會(huì)造成動(dòng)畫的卡頓。

瀏覽器向GPU發(fā)送的數(shù)據(jù)載體是什么樣?這里給出一個(gè)簡(jiǎn)單的制作載體,并把它們發(fā)送到GPU的過(guò)程。

畫每個(gè)復(fù)合層的圖像

準(zhǔn)備圖層的數(shù)據(jù)

準(zhǔn)備動(dòng)畫的著色器(如果需要)

向GPU發(fā)送數(shù)據(jù)

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

隱式合成

還記得剛才a元素和b元素動(dòng)畫的例子嗎?現(xiàn)在我們將b元素做動(dòng)畫,a元素靜止不動(dòng)。

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

所以我們現(xiàn)在有三個(gè)復(fù)合層a元素所在的復(fù)合層、b元素所在的復(fù)合層、其他內(nèi)容及背景層。

一個(gè)或多個(gè)沒有自己復(fù)合層的元素要出現(xiàn)在有復(fù)合層元素的上方,它就會(huì)擁有自己的復(fù)合層;這種情況被稱為隱式合成。

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

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