色综合图-色综合图片-色综合图片二区150p-色综合图区-玖玖国产精品视频-玖玖香蕉视频

您的位置:首頁技術(shù)文章
文章詳情頁

詳解盒子端CSS動畫性能提升

瀏覽:79日期:2022-06-02 16:25:29
目錄
  • 流暢動畫的標準
  • 盒子端動畫優(yōu)化
  • 動畫性能上報分析
  • 研究結(jié)論
  • Web 每一幀的渲染
  • 優(yōu)化動畫步驟
    • 1.精簡 DOM ,合理布局
    • 2.使用 transform 代替 left、top,減少使用耗性能樣式
    • 3.控制頻繁動畫的層級關(guān)系
    • 4. 使用 will-change 可以在元素屬性真正發(fā)生變化之前提前做好對應準備
    • 5. 使用 dev-tool 時間線 timeline 觀察,找出導致高耗時、掉幀的關(guān)鍵操作
  • 總結(jié)一下

    流暢動畫的標準

    理論上說,F(xiàn)PS 越高,動畫會越流暢,目前大多數(shù)設備的屏幕刷新率為 60 次/秒,所以通常來講 FPS 為 60frame/s 時動畫效果最好,也就是每幀的消耗時間為 16.67ms。

    直觀感受,不同幀率的體驗

    • 幀率能夠達到 50 ~ 60 FPS 的動畫將會相當流暢,讓人倍感舒適;
    • 幀率在 30 ~ 50 FPS 之間的動畫,因各人敏感程度不同,舒適度因人而異;
    • 幀率在 30 FPS 以下的動畫,讓人感覺到明顯的卡頓和不適感;
    • 幀率波動很大的動畫,亦會使人感覺到卡頓。

    盒子端動畫優(yōu)化

    在騰訊視頻客廳盒子端,Web 動畫未進行優(yōu)化之前,一些復雜動畫的幀率僅有 10 ~ 30 FPS,卡頓感非常明顯,帶來很不好的用戶體驗。

    而進行優(yōu)化之后,能將 10 ~ 30 FPS的動畫優(yōu)化至 30 ~ 60 FPS,雖然不算優(yōu)化到最完美,但是當前盒子硬件的條件下,已經(jīng)算是非常大的進步。

    盒子端 Web 動畫性能比較

    首先先給出在盒子端不同類型的Web 動畫的性能比較。經(jīng)過對比,在盒子端 CSS 動畫的性能要優(yōu)于 Javascript 動畫,而在 CSS 動畫里,使用 GPU 硬件加速的動畫性能要優(yōu)于不使用硬件加速的性能。

    所以在盒子端,實現(xiàn)一個 Web 動畫,優(yōu)先級是:

    GPU 硬件加速 CSS 動畫 > 非硬件加速 CSS 動畫 > Javascript 動畫

    動畫性能上報分析

    要有優(yōu)化,就必須得有數(shù)據(jù)做為支撐。對比優(yōu)化前后是否有提升。而對于動畫而言,衡量一個動畫的標準也就是 FPS 值。

    所以現(xiàn)在的關(guān)鍵是如何計算出每個動畫運行時的幀率,這里我使用的是requestAnimationFrame這個函數(shù)近似的得到動畫運行時的幀率。

    考慮到盒子都是安卓系統(tǒng),且大多版本較低且硬件性能堪憂,導致一是許多高級 API 無法使用,二是這里只是近似得到動畫幀率

    原理是,正常而言requestAnimationFrame這個方法在一秒內(nèi)會執(zhí)行 60 次,也就是不掉幀的情況下。假設動畫在時間 A 開始執(zhí)行,在時間 B 結(jié)束,耗時 x ms。而中間requestAnimationFrame一共執(zhí)行了 n 次,則此段動畫的幀率大致為:n / (B - A)。

    核心代碼如下,能近似計算每秒頁面幀率,以及我們額外記錄一個allFrameCount,用于記錄 rAF 的執(zhí)行次數(shù),用于計算每次動畫的幀率 :

    var rAF = function () {    return (window.requestAnimationFrame ||window.webkitRequestAnimationFrame ||function (callback) {    window.setTimeout(callback, 1000 / 60);}    );}(); var frame = 0;var allFrameCount = 0;var lastTime = Date.now();var lastFameTime = Date.now(); var loop = function () {    var now = Date.now();    var fs = (now - lastFameTime);    var fps = Math.round(1000 / fs);     lastFameTime = now;    // 不置 0,在動畫的開頭及結(jié)尾記錄此值的差值算出 FPS    allFrameCount++;    frame++;     if (now > 1000 + lastTime) {var fps = Math.round((frame * 1000) / (now - lastTime));// console.log("fps", fps); 每秒 FPSframe = 0;lastTime = now;    };     rAF(loop);}

    研究結(jié)論

    所以,我們的目標就是在使用 GPU 硬件加速的基礎之上,更深入的去優(yōu)化 CSS 動畫,先給出最后的一個優(yōu)化步驟方案:

    1.精簡 DOM ,合理布局

    2.使用 transform 代替 left、top,減少使用耗性能樣式

    3.控制頻繁動畫的層級關(guān)系

    4.考慮使用 will-change

    5.使用 dev-tool 時間線 timeline 觀察,找出導致高耗時、掉幀的關(guān)鍵操作

    下文會有每一步驟的具體分析解釋。

    Web 每一幀的渲染

    要想達到 60 FPS,每幀的預算時間僅比 16 毫秒多一點 (1 秒/ 60 = 16.67 毫秒)。但實際上,瀏覽器有整理工作要做,因此您的所有工作需要盡量在 10 毫秒內(nèi)完成。

    而每一幀,如果有必要,我們能控制的部分,也是像素至屏幕管道中的關(guān)鍵步驟如下:

    完整的像素管道JS / CSS > 樣式 > 布局 > 繪制 > 合成:

    1.JavaScript。一般來說,我們會使用 JavaScript 來實現(xiàn)一些視覺變化的效果。比如用 jQuery 的 animate 函數(shù)做一個動畫、對一個數(shù)據(jù)集進行排序或者往頁面里添加一些 DOM 元素等。當然,除了 JavaScript,還有其他一些常用方法也可以實現(xiàn)視覺變化效果,比如:CSS Animations、Transitions 和 Web Animation API。

    2.樣式計算。此過程是根據(jù)匹配選擇器(例如 .headline 或 .nav > .nav__item)計算出哪些元素應用哪些 CSS 3. 規(guī)則的過程。從中知道規(guī)則之后,將應用規(guī)則并計算每個元素的最終樣式。

    3.布局。在知道對一個元素應用哪些規(guī)則之后,瀏覽器即可開始計算它要占據(jù)的空間大小及其在屏幕的位置。網(wǎng)頁的布局模式意味著一個元素可能影響其他元素,例如 <body> 元素的寬度一般會影響其子元素的寬度以及樹中各處的節(jié)點,因此對于瀏覽器來說,布局過程是經(jīng)常發(fā)生的。

    4.繪制。繪制是填充像素的過程。它涉及繪出文本、顏色、圖像、邊框和陰影,基本上包括元素的每個可視部分。繪制一般是在多個表面(通常稱為層)上完成的。

    5.合成。由于頁面的各部分可能被繪制到多層,由此它們需要按正確順序繪制到屏幕上,以便正確渲染頁面。對于與另一元素重疊的元素來說,這點特別重要,因為一個錯誤可能使一個元素錯誤地出現(xiàn)在另一個元素的上層。

    當然,不一定每幀都總是會經(jīng)過管道每個部分的處理。我們的目標就是,每一幀的動畫,對于上述的管道流程,能避免則避免,不能避免則最大限度優(yōu)化。

    優(yōu)化動畫步驟

    先給出一個步驟,調(diào)優(yōu)一個動畫,有一定的指導原則可以遵循,一步一步深入動畫:

    1.精簡 DOM ,合理布局

    這個沒什么好說的,如果可以,精簡 DOM 結(jié)構(gòu)在任何時候都是對頁面有幫助的。

    2.使用 transform 代替 left、top,減少使用耗性能樣式

    現(xiàn)代瀏覽器在完成以下四種屬性的動畫時,消耗成本較低:

    • position(位置):transform: translate(npx, npx)
    • scale(比例縮放):transform: scale(n)
    • rotation(旋轉(zhuǎn)) :transform: rotate(ndeg)
    • opacity(透明度):opacity: 0...1

    如果可以,盡量只使用上述四種屬性去控制動畫。

    不同樣式在消耗性能方面是不同的,改變一些屬性的開銷比改變其他屬性要多,因此更可能使動畫卡頓。

    例如,與改變元素的文本顏色相比,改變元素的box-shadow將需要開銷大很多的繪圖操作。 改變元素的width可能比改變其transform要多一些開銷。如box-shadow屬性,從渲染角度來講十分耗性能,原因就是與其他樣式相比,它們的繪制代碼執(zhí)行時間過長。

    這就是說,如果一個耗性能嚴重的樣式經(jīng)常需要重繪,那么你就會遇到性能問題。其次你要知道,沒有不變的事情,在今天性能很差的樣式,可能明天就被優(yōu)化,并且瀏覽器之間也存在差異。

    開啟 GPU 硬件加速

    歸根結(jié)底,上述四種屬性的動畫消耗較低的原因是會開啟了 GPU 硬件加速。動畫元素生成了自己的圖形層(GraphicsLayer)。

    通常而言,開啟 GPU 加速的方法我們可以使用

    will-change: transform

    這會使聲明了該樣式屬性的元素生成一個圖形層,告訴瀏覽器接下來該元素將會進行 transform 變換,讓瀏覽器提前做好準備。

    使用will-change并不一定會有性能的提升,因為即使瀏覽器預料到會有這些更改,依然會為這些屬性運行布局和繪制流程,所以提前告訴瀏覽器,也并不會有太多性能上的提升。這樣做的好處是,創(chuàng)建新的圖層代價很高,而等到需要時匆忙地創(chuàng)建,不如一開始直接創(chuàng)建好。

    對于 Safari 及一些舊版本瀏覽器,它們不能識別will-change,則需要使用某種 translate 3D 進行 hack,通常會使用

    transform: translateZ(0)

    所以,正常而言,在生產(chǎn)環(huán)境下,我們可能需要使用如下代碼,開啟硬件加速:

    {    will-change: transform;    transform: translateZ(0);}

    3.控制頻繁動畫的層級關(guān)系

    動畫層級的控制的意思是盡量讓需要進行 CSS 動畫的元素的z-index保持在頁面最上方,避免瀏覽器創(chuàng)建不必要的圖形層(GraphicsLayer),能夠很好的提升渲染性能。

    OK,這里又提到了圖形層(GraphicsLayer),這是一個瀏覽器渲染原理相關(guān)的知識(WebKit/blink內(nèi)核下)。它能對動畫進行加速,但同時也存在相應的加速坑!

    簡單來說,瀏覽器為了提升動畫的性能,為了在動畫的每一幀的過程中不必每次都重新繪制整個頁面。在特定方式下可以觸發(fā)生成一個合成層,合成層擁有單獨的 GraphicsLayer。

    需要進行動畫的元素包含在這個合成層之下,這樣動畫的每一幀只需要去重新繪制這個 Graphics Layer 即可,從而達到提升動畫性能的目的。

    那么一個元素什么時候會觸發(fā)創(chuàng)建一個 Graphics Layer 層?從目前來說,滿足以下任意情況便會創(chuàng)建層:

    • 硬件加速的 iframe 元素(比如 iframe 嵌入的頁面中有合成層)
    • 硬件加速的插件,比如 flash 等等
    • 使用加速視頻解碼的<video>元素
    • 3D 或者 硬件加速的 2D Canvas 元素
    • 3D 或透視變換 (perspective、transform) 的 CSS 屬性
    • 對自己的 opacity 做 CSS 動畫或使用一個動畫變換的元素
    • 擁有加速 CSS 過濾器的元素
    • 元素有一個包含復合層的后代節(jié)點(換句話說,就是一個元素擁有一個子元素,該子元素在自己的層里)
    • 元素有一個 z-index 較低且包含一個復合層的兄弟元素

    本小點中說到的動畫層級的控制,原因就在于上面生成層的最后一條:

    元素有一個 z-index 較低且包含一個復合層的兄弟元素。

    這里是存在坑的地方,首先我們要明確兩點:

    1.我們希望我們的動畫得到 GPU 硬件加速,所以我們會利用類似transform: translateZ()這樣的方式生成一個 Graphics Layer 層。

    2.Graphics Layer 雖好,但不是越多越好,每一幀的渲染內(nèi)核都會去遍歷計算當前所有的 Graphics Layer ,并計算他們下一幀的重繪區(qū)域,所以過量的 Graphics Layer 計算也會給渲染造成性能影響。

    記住這兩點之后,回到上面我們說的坑。

    假設我們有一個輪播圖,有一個 ul 列表,結(jié)構(gòu)如下:

    <div><div>輪播圖</div><ul><li>列表li</li><li>列表li</li><li>列表li</li><li>列表li</li></ul></div>

    假設給他們定義如下 CSS:

    .swiper {    position: static;    animation: 10s move infinite;} .list {    position: relative;} @keyframes move {    100% {transform: translate3d(10px, 0, 0);    }}

    由于給.swiper添加了translate3d(10px, 0, 0)動畫,所以它會生成一個 Graphics Layer,如下圖所示,用開發(fā)者工具可以打開層的展示,圖形外的黃色邊框即代表生成了一個獨立的復合層,擁有獨立的 Graphics Layer 。

    但是!在上面的圖中,我們并沒有給下面的list也添加任何能觸發(fā)生成 Graphics Layer 的屬性,但是它也同樣也有黃色的邊框,生成了一個獨立的復合層。

    原因在于上面那條元素有一個 z-index 較低且包含一個復合層的兄弟元素。我們并不希望list元素也生成 Graphics Layer ,但是由于 CSS 層級定義原因,下面的 list 的層級高于上面的 swiper,所以它被動的也生成了一個 Graphics Layer 。

    使用 Chrome,我們也可以觀察到這種層級關(guān)系,可以看到.list的層級高于.swiper:

    所以,下面我們修改一下 CSS ,改成:

    .swiper {    position: relative;    z-index: 100;} .list {    position: relative;}

    這里,我們明確使得.swiper的層級高于.list,再打開開發(fā)者工具觀察一下:

    可以看到,這一次,.list元素已經(jīng)沒有了黃色外邊框,說明此時沒有生成 Graphics Layer 。再看看層級圖:

    此時,層級關(guān)系才是我們希望看到的,.list元素沒有觸發(fā)生成 Graphics Layer 。而我們希望需要硬件加速的.swiper保持在最上方,每次動畫過程中只會獨立重繪這部分的區(qū)域。

    總結(jié)

    這個坑最早見于張云龍發(fā)布的這篇文章CSS3硬件加速也有坑,這里還要總結(jié)補充的是:

    GPU 硬件加速也會有坑,當我們希望使用利用類似transform: translate3d()這樣的方式開啟 GPU 硬件加速,一定要注意元素層級的關(guān)系,盡量保持讓需要進行 CSS 動畫的元素的z-index保持在頁面最上方。

    Graphics Layer 不是越多越好,每一幀的渲染內(nèi)核都會去遍歷計算當前所有的 Graphics Layer ,并計算他們下一幀的重繪區(qū)域,所以過量的 Graphics Layer 計算也會給渲染造成性能影響。

    可以使用 Chrome ,用上面介紹的兩個工具對自己的頁面生成的 Graphics Layer 和元素層級進行觀察然后進行相應修改。

    上面觀察頁面層級的 chrome 工具非常吃內(nèi)存?好像還是一個處于實驗室的功能,分析稍微大一點的頁面容易直接卡死,所以要多學會使用第一種觀察黃色邊框的方式查看頁面生成的 Graphics Layer 這種方式。

    4. 使用 will-change 可以在元素屬性真正發(fā)生變化之前提前做好對應準備

    // 示例.example {    will-change: transform;}

    上面已經(jīng)提到過 will-change 了。

    will-change 為 web 開發(fā)者提供了一種告知瀏覽器該元素會有哪些變化的方法,這樣瀏覽器可以在元素屬性真正發(fā)生變化之前提前做好對應的優(yōu)化準備工作。 這種優(yōu)化可以將一部分復雜的計算工作提前準備好,使頁面的反應更為快速靈敏。

    值得注意的是,用好這個屬性并不是很容易:

    在一些低端盒子上,will-change會導致很多小問題,譬如會使圖片模糊,有的時候很容易適得其反,所以使用的時候還需要多加測試。

    不要將 will-change 應用到太多元素上:瀏覽器已經(jīng)盡力嘗試去優(yōu)化一切可以優(yōu)化的東西了。有一些更強力的優(yōu)化,如果與 will-change 結(jié)合在一起的話,有可能會消耗很多機器資源,如果過度使用的話,可能導致頁面響應緩慢或者消耗非常多的資源。

    有節(jié)制地使用:通常,當元素恢復到初始狀態(tài)時,瀏覽器會丟棄掉之前做的優(yōu)化工作。但是如果直接在樣式表中顯式聲明了 will-change 屬性,則表示目標元素可能會經(jīng)常變化,瀏覽器會將優(yōu)化工作保存得比之前更久。所以最佳實踐是當元素變化之前和之后通過腳本來切換 will-change 的值。

    不要過早應用 will-change 優(yōu)化:如果你的頁面在性能方面沒什么問題,則不要添加 will-change 屬性來榨取一丁點的速度。 will-change 的設計初衷是作為最后的優(yōu)化手段,用來嘗試解決現(xiàn)有的性能問題。它不應該被用來預防性能問題。過度使用 will-change 會導致生成大量圖層,進而導致大量的內(nèi)存占用,并會導致更復雜的渲染過程,因為瀏覽器會試圖準備可能存在的變化過程,這會導致更嚴重的性能問題。

    給它足夠的工作時間:這個屬性是用來讓頁面開發(fā)者告知瀏覽器哪些屬性可能會變化的。然后瀏覽器可以選擇在變化發(fā)生前提前去做一些優(yōu)化工作。所以給瀏覽器一點時間去真正做這些優(yōu)化工作是非常重要的。使用時需要嘗試去找到一些方法提前一定時間獲知元素可能發(fā)生的變化,然后為它加上 will-change 屬性。

    5. 使用 dev-tool 時間線 timeline 觀察,找出導致高耗時、掉幀的關(guān)鍵操作

    1)對比屏幕快照,觀察每一幀包含的內(nèi)容及具體的操作

    2)找到掉幀的那一幀,分析該幀內(nèi)不同步驟的耗時占比,進行有針對性的優(yōu)化

    3)觀察是否存在內(nèi)存泄漏

    對于 timeline 的使用用法,這里有個非常好的教程,通俗易懂,可以看看:

    瀏覽器渲染優(yōu)化 Udacity 課程

    總結(jié)一下

    對于盒子端 CSS 動畫的性能,很多方面仍處于探索中,本文大量內(nèi)容在之前文章已經(jīng)出現(xiàn)過,這里更多的是歸納總結(jié)提煉成可參照執(zhí)行的流程。

    本文的優(yōu)化方案研究同樣適用于 PC Web 及移動 Web

    以上就是詳解盒子端CSS動畫性能提升的詳細內(nèi)容,更多關(guān)于盒子端CSS動畫性能提升的資料請關(guān)注其它相關(guān)文章!

    標簽: CSS HTML
    相關(guān)文章:
    主站蜘蛛池模板: 国产精品爱久久久久久久小 | 国产精品九九视频 | 国产精品久久久久影视不卡 | 欧美视频一区 | 国产情侣真实露脸在线最新 | 中文字幕国产欧美 | 久久九九国产精品怡红院 | 久久国产成人精品麻豆 | 三级黄色免费网站 | 国产99视频免费精品是看6 | 精品国产中文一级毛片在线看 | 91av小视频 | 国产精品久久久久久久 | 国产精品高清全国免费观看 | 日本高清色本在线www | 亚洲 欧美 国产 日韩 制服 bt | 国产二区三区 | 本道久久综合88全国最大色 | 日韩国产午夜一区二区三区 | 日韩免费在线 | 精品欧美亚洲韩国日本久久 | 在线成人精品国产区免费 | 国产视频a | 精品女厕沟底拍撒尿 | 日韩一级免费毛片 | 亚洲的天堂 | 欧美成人影院在线观看三级 | 国产日韩欧美精品一区 | 一级a级国产不卡毛片 | 真人一级毛片全部免 | 美女张开腿黄网站免费国产 | 久久国产精品久久久久久 | 亚洲精品一区二区三区在线播放 | 男女免费爽爽爽在线视频 | 欧美精品在线视频 | 一区二区三区四区视频在线观看 | 毛片亚洲毛片亚洲毛片 | 蕾丝视频永久在线入口香蕉 | 亚州人成网在线播放 | 欧美日韩一区二区综合在线视频 | 色拍拍在精品视频69影院在线 |