這是瀏覽器渲染引擎的處理過程:
1.接收到文檔后午绳,渲染引擎會對HTML文檔進(jìn)行解析生成DOM樹、對CSS文件進(jìn)行解析生成CSSOM樹映之;
2.同時(shí)執(zhí)行頁面中的JavaScript代碼拦焚;
3.最終根據(jù)DOM樹和CSSOM樹,計(jì)算樣式(Caluclate Style)生成渲染樹杠输,【渲染樹中赎败,只會包含即將顯示在頁面中的元素及其樣式信息(如head元素、display為hidden的元素就不會包含在渲染樹中)】抬伺;
4.根據(jù)渲染樹需要進(jìn)行布局(layout)來計(jì)算每個(gè)元素在頁面上的位置螟够;
5.接下來渲染引擎開始進(jìn)行繪制(paint),這一步分為若干階段:
5.1 根據(jù)渲染樹將每層(layer)的各個(gè)元素繪制
5.2然后將繪制出的若干連續(xù)圖像進(jìn)行柵格化(Rasterization)峡钓,最后將柵格化后的圖像合成(composite)為要顯示在屏幕上的圖像妓笙。
5.3對每一層的繪制是由瀏覽器來完成的;最后的合成是由GPU來完成能岩;而柵格化過程取決于瀏覽器的設(shè)置寞宫,chrome默認(rèn)開啟GPU柵格化,否則由CPU進(jìn)行拉鹃。
當(dāng)首次將DOM樹構(gòu)建完成后辈赋,每次頁面發(fā)生改變時(shí)進(jìn)行進(jìn)行的主要流程為:
javascript -->style-->;ayout -->paint -->compsite
其中CSS動畫不會調(diào)用JavaScript,我們知道膏燕,在渲染中主要消耗時(shí)間的是Layout/Reflow和Paint/Repaint的過程钥屈,因此要盡量避免和減少這兩個(gè)階段的時(shí)間。
cpu 和 gpu 的區(qū)別
cpu 是通用的坝辫,能夠執(zhí)行各種邏輯和運(yùn)算篷就,而 gpu 則是主要是用于并行計(jì)算大批量的重復(fù)任務(wù),不能處理復(fù)雜邏輯近忙。
cpu 中控制器和緩存占據(jù)了很大一部分竭业,而 gpu 中這兩部分則很少智润,但是有更多的核心用于計(jì)算。
兩者對比的話未辆,cpu 相當(dāng)于一個(gè)大學(xué)生窟绷,能夠解決各種難題,但是計(jì)算 1 萬個(gè)加法就沒那么快咐柜,而 gpu 就像一幫小學(xué)生兼蜈,解決不了難題,但是計(jì)算加法這種就很快炕桨,因?yàn)槿硕唷?/p>
也就是說如果邏輯復(fù)雜饭尝,那么只能用 cpu,如果只是計(jì)算量大献宫,并且每個(gè)計(jì)算都比較重復(fù),那就比較適合 gpu实撒。
3d 的渲染中有大量這種重復(fù)卻簡單的計(jì)算姊途,比如頂點(diǎn)數(shù)據(jù)和光柵化的像素?cái)?shù)據(jù),通過 gpu 就可以并發(fā)的一次計(jì)算成百上千個(gè)知态。
瀏覽器在處理下面的 css 的時(shí)候捷兰,會使用 gpu 渲染:
transform 、opacity 负敏、filter贡茅、 will-change
瀏覽器是把內(nèi)容分到不同的圖層分別渲染的,最后合并到一起其做,而觸發(fā) gpu 渲染會新建一個(gè)圖層顶考,把該元素樣式的計(jì)算交給 gpu。
opacity 需要改變每個(gè)像素的值妖泄,符合重復(fù)且大量的特點(diǎn)驹沿,會新建圖層,交給 gpu 渲染蹈胡。transform 是動畫渊季,每個(gè)樣式值的計(jì)算也符合重復(fù)且大量的特點(diǎn),也默認(rèn)會使用 gpu 加速罚渐。同理 fiter 也是一樣
却汉。
這里要注意的是 gpu 硬件加速是需要新建圖層的,而把該元素移動到新圖層是個(gè)耗時(shí)操作荷并,界面可能會閃一下合砂,所以最好提前做。will-change 就是提前告訴瀏覽器在一開始就把元素放到新的圖層璧坟,方便后面用 gpu 渲染的時(shí)候既穆,不需要做圖層的新建赎懦。
當(dāng)然,有的時(shí)候我們想強(qiáng)制觸發(fā)硬件渲染幻工,就可以通過上面的屬性励两,比如
will-change: transform;
transform:translate3d(0, 0, 0);
SVG圖表動畫性能的優(yōu)化
對于一般的HTML的元素,遵循上述的方法就可以了囊颅,但有一些大屏中的圖表是使用SVG元素來繪制的当悔,由于并不是標(biāo)準(zhǔn)的DOM元素,Chrome并不能支持SVG元素的硬件加速踢代,即使設(shè)置了transform盲憎、will-change等屬性,單個(gè)的SVG元素也不能作為單獨(dú)的層進(jìn)行繪制胳挎。
對于有多個(gè)圖表的頁面饼疙,其中只有一個(gè)圖表有動畫,在動畫過程中慕爬,如果只有一個(gè)圖層窑眯,那么需要將整個(gè)圖層進(jìn)行重繪,雖然現(xiàn)在Chrome已經(jīng)可以智能選擇最小的重繪區(qū)域進(jìn)行增量繪制医窿,但當(dāng)圖層較大時(shí)這樣的判斷也會造成一定的開銷磅甩。因此可以考慮將有動畫的圖標(biāo)單獨(dú)放在一個(gè)圖層中,其他沒有動畫的圖表和頁面是的其他元素仍然和背景在一個(gè)圖層中姥卢。
在實(shí)際使用中卷要,可以在某張圖表需要動畫時(shí),設(shè)置SVG標(biāo)簽的will-change屬性独榴,將SVG元素提升到獨(dú)立的層僧叉,來減少動畫繪制時(shí)間;而圖表沒有動畫時(shí)則不需要特別處理括眠,避免過度繪制造成的內(nèi)存占用增加和圖層傳輸彪标、合成時(shí)間的增加。
一些優(yōu)化建議
1掷豺、動畫使用 transform 實(shí)現(xiàn)
對于一些體驗(yàn)要求較高的關(guān)鍵動畫捞烟,比如一些交互復(fù)雜的玩法頁面,存在持續(xù)變化位置的 animation 元素当船,我們最好是使用 transform 來實(shí)現(xiàn)而不是通過改變 left/top 的方式题画。這樣做的原因是,如果使用 left/top 來實(shí)現(xiàn)位置變化德频,animation 節(jié)點(diǎn)和 Document 將被放到了同一個(gè) GraphicsLayer 中進(jìn)行渲染苍息,持續(xù)的動畫效果將導(dǎo)致整個(gè) Document 不斷地執(zhí)行重繪,而使用 transform 的話,能夠讓 animation 節(jié)點(diǎn)被放置到一個(gè)獨(dú)立合成層中進(jìn)行渲染繪制竞思,動畫發(fā)生時(shí)不會影響到其它層表谊。并且另一方面,動畫會完全運(yùn)行在 GPU 上盖喷,相比起 CPU 處理圖層后再發(fā)送給顯卡進(jìn)行顯示繪制來說爆办,這樣的動畫往往更加流暢。
2课梳、減少隱式合成
雖然隱式合成從根本上來說是為了保證正確的圖層重疊順序距辆,但具體到實(shí)際開發(fā)中,隱式合成很容易就導(dǎo)致一些無意義的合成層生成暮刃,歸根結(jié)底其實(shí)就要求我們在開發(fā)時(shí)約束自己的布局習(xí)慣跨算,避免踩坑。
我們在平時(shí)開發(fā)中不注意可能造成頁面生成了過多的合成層椭懊,之前有一個(gè)項(xiàng)目诸蚕,在pc端查看是存在明顯卡頓,利用 Chrome Devtools 分析之后發(fā)現(xiàn)灾搏,頁面里邊存在的一個(gè)帶動畫 transform 的 button 按鈕挫望,提升為了合成層,動畫交疊的不確定性使得頁面內(nèi)其他 z-index
大于它但其實(shí)并沒有交疊的節(jié)點(diǎn)也都全部提升為了合成層(這個(gè)原因真的好坑)狂窑。
這個(gè)時(shí)候我們只需要把這個(gè)動畫節(jié)點(diǎn)的 z-index
屬性值設(shè)置得大一些,讓層疊順序高過于頁面其他無關(guān)節(jié)點(diǎn)就行桑腮。當(dāng)然并不是盲目地設(shè)置 z-index
就能避免泉哈,有時(shí)候 z-index
也還是會導(dǎo)致隱式合成,這個(gè)時(shí)候可以試著調(diào)整一下文檔中節(jié)點(diǎn)的先后順序直接讓后邊的節(jié)點(diǎn)來覆蓋前邊的節(jié)點(diǎn)破讨,而不用 z-index
來調(diào)整重疊關(guān)系丛晦。方法不是唯一的,具體方式還是得根據(jù)不同的頁面具體分析提陶。
改善后的頁面效果如下烫沙,可以看到相比優(yōu)化前,我們消除了很多無意義的合成層隙笆。
3锌蓄、減小合成層的尺寸
舉個(gè)簡單的例子,分別畫兩個(gè)尺寸一樣的 div撑柔,但實(shí)現(xiàn)方式有點(diǎn)差別:一個(gè)直接設(shè)置尺寸 100x100瘸爽,另一個(gè)設(shè)置尺寸 10x10,然后通過 scale
放大 10 倍铅忿,并且我們讓這兩個(gè) div 都提升為合成層:
<style>
.bottom, .top {
position: absolute;
will-change: transform;
}
.bottom {
width: 100px;
height: 100px;
top: 20px;
left: 20px;
z-index: 3;
background: rosybrown;
}
.top {
width: 10px;
height: 10px;
transform: scale(10);
top: 200px;
left: 200px;
z-index: 5;
background: indianred;
}
</style>
<body>
<div class="bottom"></div>
<div class="top"></div>
</body>
利用 Chrome Devtools 查看這兩個(gè)合成層的內(nèi)存占用后發(fā)現(xiàn)剪决,.bottom
內(nèi)存占用是 39.1 KB,而 .top
是 400 B,差距十分明顯柑潦。這是因?yàn)?.top
是合成層享言,transform 位于的 Composite 階段,現(xiàn)在完全在 GPU 上執(zhí)行渗鬼。因此對于一些純色圖層來說览露,我們可以使用 width 和 height 屬性減小合成層的物理尺寸,然后再用 transform: scale(…)
放大乍钻,這樣一來可以極大地減少層合成帶來的內(nèi)存消耗肛循。