为什么过渡框阴影会导致整页重绘?

Leo*_*ang 6 html css css3 google-chrome-devtools css-transitions

当我在一个带有动画的元素上盘旋时,我注意到我的页面已经滞后了box-shadow.使用Chrome的Devtools,我注意到当我在元素上盘旋时整个页面都被重新绘制.重绘时间超过40毫秒,约为3帧.在transition持续大约半秒,所以在半秒有明显的滞后.

如何将重绘限制为仅具有框阴影的区域?

这是一个演示:http://jsfiddle.net/8sa41xfL/

html,body{
    height:100%;
}

#test{
    background:red;
    height:100px;
    width:200px;
    transition:box-shadow 0.5s;
}

#test:hover{
    box-shadow:0 0 3px 3px rgba(0,0,0,0.3);
}
Run Code Online (Sandbox Code Playgroud)
<div id=test></div>
Run Code Online (Sandbox Code Playgroud)

transform:translateZ(0)在我的页面上不起作用,但它适用于小提琴.还有另外一个解决方案transform:translateZ(0)吗?

Har*_*rry 6

正如皮埃尔在答案box-shadow中所提到的线程所提到的那样,绘画很昂贵.解释为什么它很昂贵需要深入了解渲染的工作方式,而我没有足够的知识来完全解释它.但是这个答案试图解释为什么整个页面被重新绘制以及各种可能的方法来避免它.


根据CSS Triggers网站:

更改框阴影不会触发任何几何体更改,这很好.但由于它是一种视觉特性,它会导致绘画.绘画通常是一种超级昂贵的操作,所以你应该谨慎.

一旦绘制了任何像素,页面将被合成在一起.


为什么每次都要重新粉刷整个页面?

以下文章解释了绘画在高层次上的实际运作方式:

基于这些文章,我们可以看到DOM树中生成可视输出的每个节点都被视为RenderObject,并且每个RenderObject直接或间接地是RenderLayer的一部分.每当发生更改时,渲染器(或渲染对象)都会使其屏幕上的矩形(或RenderLayer)无效并触发重绘.

在这种情况下,似乎整个页面都被重新绘制,因为#test元素不保证创建单独的RenderLayer(基于Chromium Project文章中提到的标准),因此成为根渲染层的一部分.因为它是根渲染层的一部分,所以每次需要重绘时整个页面都会重新绘制.

下面的代码片段证明上述论断是正确的.在这里,我添加了一个#cover元素(带定位)来封闭#test元素.现在,由于#cover元素具有显式定位,因此它在根层上方创建了一个额外的层,并#test成为此中间层的一部分.现在,我们可以看到box-shadow转换只重绘了这个中间层而不是整个页面.

html,
body {
  height: 100%;
}
#cover {
  position: relative;
}
#test {
  background: red;
  height: 100px;
  width: 200px;
  transition: box-shadow 0.5s;
}
#test:hover {
  box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.3);
}
Run Code Online (Sandbox Code Playgroud)
<div id=cover>
  <div id=test></div>
</div>
Run Code Online (Sandbox Code Playgroud)


解决办法是什么?

可以使用各种CSS属性来解决这个问题,但它们似乎都指向高级别的相同点 - 也就是说,为#test元素创建单独的渲染层.

以下是为#test元素创建单独渲染层的一些可能选项:

  • 通过添加显式位置属性 - 这与Pierre的答案中描述的选项相同,但absolute定位不是唯一的选择.即使relative定位也能解决它.

    html,
    body {
      height: 100%;
    }
    #test {
      position: relative;
      background: red;
      height: 100px;
      width: 200px;
      transition: box-shadow 0.5s;
    }
    #test:hover {
      box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.3);
    }
    Run Code Online (Sandbox Code Playgroud)
    <div id=test></div>
    Run Code Online (Sandbox Code Playgroud)

  • 通过添加透明度(不透明度) - 浏览器似乎甚至可以opacity: 0.99视为添加透明度而非常有用,因为添加它不会导致任何视觉差异.

    html,
    body {
      height: 100%;
    }
    #test {
      background: red;
      height: 100px;
      width: 200px;
      opacity: 0.99;
      transition: box-shadow 0.5s;
    }
    #test:hover {
      box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.3);
    }
    Run Code Online (Sandbox Code Playgroud)
    <div id=test></div>
    Run Code Online (Sandbox Code Playgroud)

  • 通过添加一个虚拟CSS过滤器 - 我们可以添加一个,filter: blur(0px)因为它什么都不做.

    html,
    body {
      height: 100%;
    }
    #test {
      background: red;
      height: 100px;
      width: 200px;
      -webkit-filter: blur(0px);
      filter: blur(0px);
      transition: box-shadow 0.5s;
    }
    #test:hover {
      box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.3);
    }
    Run Code Online (Sandbox Code Playgroud)
    <div id=test></div>
    Run Code Online (Sandbox Code Playgroud)


Pie*_*rre 5

CSS框阴影的绘制成本很高.了解更多关于SO 这里.

如果要避免整页重绘,请position:absolute在元素上使用a .这将重新绘制元素周围的区域,而不会影响整个页面.小提琴.