当我将鼠标悬停在具有动画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);
}
<div id=test></div>
transform:translateZ(0)
在我的页面上不起作用,但它起作用。除了transform:translateZ(0)
之外,还有其他修复程序吗?
正如皮埃尔答案中链接的线程中所提到的,box-shadow
的绘制成本很高。解释为什么它很昂贵需要深入了解渲染的工作方式,而我没有足够的知识来完全解释它。但这个答案试图解释为什么整个页面都被重新绘制,以及避免它的各种可能方法
根据CSS触发器网站:
更改长方体阴影不会触发任何几何体更改,这很好。但由于它是一种视觉特性,它将导致绘画的发生。喷漆通常是一项超昂贵的操作,因此您应该谨慎。
一旦绘制了任何像素,页面将被合成在一起。
为什么每次都要重新绘制整个页面
以下文章解释了绘画在高水平上的实际工作方式:
- HTML5岩石-浏览器如何工作-绘画
- Chromium项目-GPU在Chrome中的加速渲染
基于这些文章,我们可以看到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);
}
<div id=cover>
<div id=test></div>
</div>
解决方案是什么
有各种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); }
<div id=test></div>
-
通过添加透明度(不透明度(-浏览器似乎甚至将
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); }
<div id=test></div>
-
通过添加一个伪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); }
<div id=test></div>
CSS框阴影的绘制成本很高。点击此处阅读更多关于SO的信息。
如果希望避免重新绘制整页,请在元素上使用position:absolute
。这将在不影响整个页面的情况下重新绘制元素周围的区域。不停摆弄