關於CSS will-change 屬性你須要知道的事

不知道你有沒有注意到,在基於Webkit的瀏覽器上執行某些CSS操做時頁面會出現不流暢或者閃一下的狀況,尤爲是執行CSS動畫的時候,那你以前可能已經聽過「硬件加速(hardware acceleration)」這個專業術語了。css

CPU&GPU&硬件加速(Hardware Acceleration)

簡單來講硬件加速意味着瀏覽器會幫你把一部分對渲染頁面來講較繁重的任務交給GPU(Graphics Processing Unit),而不是一股腦都交給CPU(Central Processing Unit )去處理,當這部分CSS操做獲得硬件加速時,可使頁面渲染速度更快。git

CPU在電腦主板上,就像是計算機的」大腦「,CPU幾乎會作全部事,GPU位於計算機的顯卡上,主要用作圖形渲染,此外,GPU是專爲執行圖形渲染所需的複雜數學和幾何計算而設計的,因此將一些操做移到GPU上去處理能夠帶來巨大的性能提高而且減輕了CPU的壓力,在移動端尤其明顯。github

硬件加速(又名GPU加速)依賴於瀏覽器在渲染頁面時使用的分層模型,當對頁面上的元素執行某些操做(例如3D變換),對應元素會被提高到到本身的圖層,獨立於頁面的其他部分的呈現並在後期合成(繪製到屏幕上),這樣單獨把元素隔離,可使得當頁面上只有這個元素髮生變換(transform)的時候其他元素不須要從新渲染,從而帶來速度提高的優勢,值得一提的是,只有3D變換纔有資格擁有本身的圖層,2D變換則沒有。瀏覽器

CSS animation,transform,transition這些屬性並不會自動被硬件加速,而是由瀏覽器的渲染引擎去執行的,可是一些瀏覽器經過某些屬性提供硬件加速,從而提高頁面的渲染性能。例如CSS中的opacity屬性是少數幾個能夠被瀏覽器認定爲可被硬件加速的屬性之一,由於GPU能夠很容易的實現。通常來講,任何想要經過CSS transition或動畫淡化不透明度的圖層的行爲,瀏覽器會把它丟給GPU去處理從而提升處理速度。全部的CSS屬性中opacity是性能最好屬性的之一,其餘常見的硬件加速操做是CSS 3D變換緩存

Hack方法來硬件加速:translateZ() 或者 translate3d()app

使用translateZ()(或translate3d())這種hack方式(有時也稱爲null變換hack)來讓瀏覽器對animationtransform行爲使用硬件加速,經過向一個不會在三維空間中轉換的元素添加簡單的3D變換來實現硬件加速。例如經過給一個二維空間中動畫添加簡單的規則來硬件加速。ide

transform:translate3d(0,0,0)

硬件加速操做會建立所謂的合成層,合成層會被上傳到GPU並由GPU合成,可是這種hack的方法去建立圖層並非萬能的,圖層建立能夠加快頁面加載速度,但會帶來其餘的成本:它會佔用系統RAM和GPU上的內存(限於移動設備),而且不少時候都會帶來不良影響(特別是在移動設備上),因此這種方法要合理的去使用,你必需要清楚的知道使用這種方式是否是真的能夠提升頁面性能,不能使這個操做反而成了影響頁面性能的瓶頸。性能

除了這種建立圖層的方法,CSS引入了一個新的屬性,它容許咱們提早告知瀏覽器可能會對元素進行哪些操做,讓瀏覽器去優化並提早處理那些潛在的比較消耗性能的操做好比在動畫開始以前,提早處理元素的動畫行爲。這個屬性就是will-change優化

新屬性will-change的榮光

will-change屬性容許你提早通知瀏覽器你可能會對某個元素作什麼類型的操做,以便於瀏覽器在須要的時候採起適當的優化方案。所以,避免了可能對頁面的響應性產生負面影響的非必要成本,使元素能夠更快地呈現。渲染並快速更新,從而得到更流暢的體驗。動畫

舉個例子,當對元素使用CSS transform時,元素及其內容可能會提高爲圖層,如以前所言,以後會將他們合成(composited)(繪製在屏幕上),可是將一個元素提高到一個新圖層是很消耗性能的,這可能會使transform動畫的開始延遲明顯的幾分之一秒,從而引發明顯的「閃爍」。

爲了不這種狀況發生,咱們能夠提早告知瀏覽器,讓瀏覽器能夠提早作準備,那麼當一樣的操做發生時,由於元素的圖層已準備就緒,而後就能夠馬上執行轉換動畫,從而渲染元素並快速更新頁面。

使用will-change能夠提示瀏覽器將會發生的轉化,語法很是簡單

will-change:transform

也能夠添加多個你指望改變的確切屬性的值。如:

will-change:transform,opacity

確切指定你想改變屬性可讓瀏覽器更好的決策如何以更優的辦法處理這些變更,這明顯是比使用hack手法讓瀏覽器被迫建立可能無用的圖層的更好且更有效的一種方法了

will-change 是否會給當前元素帶來其餘反作用

是,也不是,這取決於你給定的屬性。若是該屬性的任何非初始值將在元素上建立堆棧上下文,則在will-change中指定該屬性將在元素上建立堆棧上下文。舉個例子:將clip-path屬性和opecity屬性用於初始值之外的值時,都會在它們所應用的元素上建立堆棧上下文。所以,使用這些屬性中的一個(或兩個)做爲will-change的值,即便在更改實際發生以前,也會在元素上建立堆疊上下文,這一樣適用於將在元素上建立堆棧上下文的其餘屬性

一樣,某些屬性可能致使爲固定位置的元素建立一個包含塊。例如,一個轉換後的元素爲其全部定位子孫元素建立一個包含塊,即便那些已被設置爲position:fixed的元素。所以,若是某個屬性會致使建立一個包含塊,那麼將其指定爲will-change的值也將致使爲固定位置元素生成包含塊,因此如前所述,某些will-change屬性的變化的致使建立新的合成層,可是,GPU不支持大多數瀏覽器中CPU所支持的亞像素抗鋸齒功能,因此有時會致使內容模糊(尤爲是文本)

另外will-change屬性不會直接影響對應元素,它只是給瀏覽器打個預防針,讓瀏覽器以更高效的渲染方式去呈現元素內容,除了在以前提到的一些狀況下建立堆棧上下文和包含塊外,它對對應元素沒有直接影響。

使用will-change的注意事項

不要使用will-change去嘗試優化一切操做,有時候相反會帶來副作用,要更加聰明且合理的使用它,will-change也有一些沒法直接檢測到的反作用,畢竟這也只是一種和瀏覽器後臺通話的方式而已,不能期望它爲你作全部事情,由於使用此屬性時,請牢記如下幾點,以確保在最大程度上利用該屬性,同時避免因濫用該屬性而帶來的危害。

  • 不要使用will-change聲明對太多屬性或元素的更改

    如同以前所提到的 直接讓瀏覽器針對全部元素上的全部屬性可能發生的更改進行優化的想法是很誘人的,因此可能會寫出以下代碼

    *,
    *::before,
    *::after {
        will-change: all;
    }

    看上去好像很完美,但實際上是有很大的問題的,首先all不是合法的will-change的值,其次這樣籠統的規則根本就沒什麼用,這就好像在告訴瀏覽器這些全部的屬性均可能變化且都須要要優化,那瀏覽器徹底和沒優化的版本處理沒什麼區別,由於沒什麼優先級也沒有任何有用信息。而且如咱們以前提到的hack方法,瀏覽器已經在本身作優化了,因此這樣寫沒有任何意義,並且不可忽視的是由於瀏覽器要處理will-change相關的屬性也會佔用大量計算機資源,過分使用反而使得頁面卡頓甚至崩潰

  • 給瀏覽器足夠的時間來工做

    will-change屬性顧名思義:只告知瀏覽器即發生的變化,因此這個階段什麼改變也沒發生,咱們使用will-change是以便於瀏覽器對咱們聲明的更改進行某些優化,瀏覽器須要時間去處理優化這些更改,爲了在這些變化在真正發生時能夠當即生效,因此在元素更改以前當即設置will-change幾乎沒有任何效果,而且可能比不設置更糟糕,舉個例子:

    .element:hover {
        will-change: transform;
        transition: transform 2s;
        transform: rotate(30deg) scale(1.5);
    }

    這至關告訴瀏覽器對已經發生的變化進行優化,這徹底沒有做用,並且也不符合will-change的定義,你應該找到一種方法,至少能夠提早一點時間預測某種改變會發生,而後設置will-change的值,舉個例子,當點擊一個元素時發生變化,而後在該元素懸停時設置will-change屬性,這樣將爲瀏覽器提供足夠的時間來優化該更改,從懸停元素到用戶實際單擊元素之間的時間足以使瀏覽器進行優化了。

.element {
    /* style rules */
    transition: transform 1s ease-out;
}
.element:hover {
    will-change: transform;
}
.element:active {
    transform: rotateY(180deg);
}

可是若是咱們指望的是在鼠標懸停時變化要怎麼作呢?正如咱們所提到的,上面的代碼也是無用的。在這種狀況下,一般仍然能夠找到某種方法來在動做發生以前對其進行預測,例如懸停在目標元素的祖先元素上能夠提供瀏覽器足夠的反應執行時間,由於將鼠標懸停在其祖先元素上並不必定老是代表該元素將與之交互,所以這個階段,能夠執行諸如設置will-change屬性之類的操做

.element {
    transition: opacity .3s linear;
}
/* declare changes on the element when the mouse enters / hovers its ancestor */
.ancestor:hover .element {
    will-change: opacity;
}
/* apply change when element is hovered */
.element:hover {
    opacity: .5;
}
  • 變動生效後移除掉will-change

    瀏覽器對即將發生的更改進行的優化一般成本很高,並且正如咱們前面提到的,它會佔用計算機的大部分資源,瀏覽器進行優化的一般行爲是刪除這些優化並儘快恢復到正常行爲。可是,will-change會覆蓋此行爲,從而使優化保持的時間比瀏覽器本來的時間要長得多,正由於如此在使用完後要記得移除will-change來釋放資源

    若是在樣式中直接寫死will-change,聲明則沒法直接移除,因此基本推薦使用JS來處理,經過腳本,能夠在瀏覽器中聲明你的更改,而後在更改完成後經過偵聽更改完成的時間來移除will-change,舉個例子:如咱們以前提到的能夠經過監聽是否懸停在對應元素(或其祖先元素)上的mouseEnter事件中來設置will-change,若是你要對元素進行動畫處理,則可使用DOM事件animationEnd來偵聽動畫什麼時候結束,而後在animationEnd觸發後移除will-change

    // Rough generic example
    // Get the element that is going to be animated on click, for example
    var el = document.getElementById('element');
    
    // Set will-change when the element is hovered
    el.addEventListener('mouseenter', hintBrowser);
    el.addEventListener('animationEnd', removeHint);
    
    function hintBrowser() {
        // The optimizable properties that are going to change
        // in the animation's keyframes block
        this.style.willChange = 'transform, opacity';
    }
    
    function removeHint() {
        this.style.willChange = 'auto';
    }
  • 有選擇性的在CSS中直接使用will-change

    如以前提到的will-change被用來向瀏覽器提示一個元素將在幾毫秒內發生的更改,這是容許will-change直接寫在css中的用例之一,儘管咱們建議使用JavaScript來設置和取消will-change,可是在某些狀況下,在css中進行設置(並保留)是更好的選擇。

    一個例子:在少數可能被用戶反覆交互且應該以快速的方式響應用戶的互動的元素上設置will-change,由於元素數量有限,這就意味着瀏覽器進行的優化不會被過分使用,所以不會形成太大的傷害,例如:經過在用戶請求時將其滑出來轉換邊欄。以下規則就很合適:

    .sidebar {
        will-change: transform;
    }

    另外一個例子是在幾乎不斷變化的元素上使用will-change 好比一個元素響應用戶的鼠標移動,並隨着鼠標移動在屏幕上移動,這種狀況下直接在css中聲明will-change的值就能夠。由於它準確地描述了元素將有規律/不斷地變化,所以應保持優化狀態

    .annoying-element-stuck-to-the-mouse-cursor {
        will-change: left, top;
    }
  • will-change合法值

    will-change屬性可取四種可能的值:auto, scroll-position,contents,<custom-ident>

    <custom-ident>值用於指定你但願更改的一個或多個屬性的名稱,多個屬性值之間用逗號分隔,以下是合法的will-change屬性名稱的聲明

    will-change: transform;
    will-change: opacity;
    will-change: top, left, bottom, right;

    <custom-ident>值不包括will-change, none, all, auto,scroll-position, contents,因此如同文章以前提到的will-change:all不是合法的屬性值會被瀏覽器忽略,

    auto則表示沒有特定的意圖,意味着瀏覽器不會作任何特殊處理

    scoll-position 顧名思義,表示你指望未來隨時能夠更改元素的滾動位置,這個值頗有用,由於在使用時,瀏覽器將準備並呈現超出可滾動元素的滾動窗口中可見內容的內容。瀏覽器一般僅在滾動窗口中渲染內容,而某些內容超過該窗口,平衡了因跳過渲染而節省的內存和時間,從而使滾動看起來順滑,使用will-change:scroll-position,它能夠進行進一步的渲染優化,以即可以平滑地完成更長和或更快的內容滾動。

contents表示元素的內容可能會發生變化,瀏覽器一般會對元素作緩存,由於大部分狀況下元素不會常常性發生變化,有時候僅僅只是位置的更改。這個值用來做爲告訴瀏覽器減小或者避免對指定元素進行緩存的信號。由於若是元素的內容不斷的變化,那麼保留內容的緩存將毫無用處而且浪費時間,由於只要元素的內容發生更改,瀏覽器就會中止緩存並繼續從頭開始渲染該元素。
如同咱們以前提到的。若是在will-change中指定某些屬性,這些屬性將無效,由於瀏覽器不會對大多數屬性的更改進行任何特殊的優化。

瀏覽器支持狀況

原文連接: 關於CSS will-change 屬性你須要知道的事

相關文章
相關標籤/搜索