关于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 属性你须要知道的事

相关文章
相关标签/搜索