高性能網(wǎng)頁動(dòng)畫

渲染流程
在開發(fā)者工具的timeline里,我們可以看到一個(gè)典型的渲染過程基本如下:
Recalculate Style: 計(jì)算(應(yīng)用到元素上的)樣式。
Layout:為(渲染樹上)每個(gè)元素生成幾何形狀(大小和位置)。
Paint:為每個(gè)元素填充像素到layer。
Composite Layers : 把所有l(wèi)ayer繪制,輸出到屏幕。
渲染小結(jié)
渲染主要三階段:Layout計(jì)算范圍,Paint計(jì)算展現(xiàn),Composite合成Bitmap。
修改不同CSS屬性會(huì)觸發(fā)不同階段。比如width,height,margin,left/top等等會(huì)觸發(fā)layout;box-shadow,border-radius,background,outline等等觸發(fā)paint;transform,opacity觸發(fā)composite layer。
觸發(fā)的階段越前,渲染的代價(jià)越高。
硬件加速(GPU加速)
術(shù)語:texture??煽醋龇旁贕PU上的位圖
GPU擅長對texture進(jìn)行偏移,縮放,旋轉(zhuǎn),更改透明度
Layer模型
瀏覽器根據(jù)CSS屬性為元素生成Layers。
將Layers作為texture上傳到GPU。
改變Layer的transform,opacity屬性時(shí),渲染會(huì)跳過Layout和paint階段,直接通知GPU對Layer做變換。
Layer觸發(fā)條件
3d或perspective transform屬性
使用animation,transition改變opacity,transform的元素
video
canvas
flash
css filters
滿足上述某個(gè)條件,瀏覽器就會(huì)單獨(dú)創(chuàng)建一個(gè)layer。
硬件加速節(jié)省了哪些時(shí)間
CPU進(jìn)行Layout,Paint的時(shí)間
CPU向GPU傳輸位圖的時(shí)間
流暢動(dòng)畫
什么叫流暢?60fps,即要在16.7ms內(nèi)把一幀準(zhǔn)備好。
想要流暢需考慮兩個(gè)問題:
開始渲染的時(shí)機(jī)
渲染一幀的時(shí)間
渲染時(shí)機(jī)
setTimeout不夠精確
依靠瀏覽器內(nèi)置時(shí)鐘更新頻率。IE8及以前更新間隔為15.6,setTimeout 16.7意味著需要兩個(gè)15.6才能觸發(fā),超出14.5Ms。
main thread隊(duì)列
setTimeout不夠精確會(huì)導(dǎo)致丟幀,因?yàn)槠聊凰⑿骂l率60HZ是不變的。比如設(shè)置setTimeout 16,每隔一段時(shí)間后,就會(huì)有丟幀。
requestAnimationFrame
定義繪制每一幀前的工作。requestAnimationFrame(callback)
自動(dòng)調(diào)節(jié)頻率。callback工作太多無法在一幀內(nèi)完成,會(huì)自動(dòng)降為30fps,降頻比丟幀好。
渲染一幀的時(shí)間
從Layout來減小渲染時(shí)間
觸發(fā)Layout
更改class,導(dǎo)致width,height,margin等size,position相關(guān)屬性改變
讀取size,position相關(guān)屬性。一般瀏覽器會(huì)批量更新,但你讀取時(shí)為保證你讀取的屬性正確,強(qiáng)制進(jìn)行一次Layout。
讀取以下屬性會(huì)引起Layout:
clientHeight/Left/Top/Width, focus(),getBoundingClientRect(),getClientRects(),innerText,offsetHeight/Left/Top/Width/Parent,outerText(),scrollByLines(),scrollByPages(),scrollHeight/Left/Top/Width,scrollIntoView()...盡量不觸發(fā)Layout
用transform代替top/left
不要頻繁Layout
var h1 = el1.clientHeight;el1.style.height = (h1 + 4)+ 'px'; // 等待layoutvar h2 = el2.clientHeight; // 讀屬性,強(qiáng)制layoutel2.style.height = (h2 + 4)+ 'px'; // 等待layoutvar h3 = el3.clientHeight; // 讀屬性,強(qiáng)制layoutel3.style.height = (h3 + 4)+ 'px'; // 等待layout
分離讀寫操作,比如用requestAnimationFrame把寫操作推到下一幀。
Layout小結(jié)
不但改變CSS可能導(dǎo)致Layout,讀取位置大小相關(guān)的屬性也會(huì)導(dǎo)致Layout
分離讀寫,減少Layout
面對解偶代碼,使用requestAnimationFrame推遲的方法分離讀寫。
從Paint來減小渲染時(shí)間
觸發(fā)Paint
修改box-shadow,border-radius,color等展示相關(guān)屬性時(shí),會(huì)觸發(fā)Paint
Paint代價(jià)
box-shadow等Paint代價(jià)昂貴。
減少不必要的Paint
gif圖即使被其它Layer蓋住,也可能導(dǎo)致Paint,不必要時(shí)應(yīng)該將gif圖的display設(shè)置none
減小Paint區(qū)域
