原因:
这似乎是由于 Webkit 中的层创建和加速渲染过程导致。在开发工具中启用“显示绘制矩形”和“显示合成图层边框”选项后,查看此回答中的所有演示。
运行任何一个演示时,你会看到一些绿色和橙色的框。绿色框是绘制矩形,而橙色框是由渲染引擎为加速渲染创建的合成图层。在渲染过程中,WebKit(和Blink)并不总是重新绘制整个页面。只有受影响的页面区域(层)会重新绘制(以提高性能)。
使用浮动:
在此代码片段中,你会发现渲染引擎创建了一个绘制矩形和一个页面的合成图层以及一个 span
元素(“Some Content”)的合成图层。由于 span
是一个 inline
元素,它不会生成包含其后代框和生成内容的主块级框。根据我的理解,这使伪元素相对于根元素浮动。这也意味着伪元素在屏幕上的位置不取决于父 span
元素(实际上,如果你给 span
设定一个负边距,你会发现内容重叠,而如果为 span
设置了 display: block
,负边距也会将伪元素的内容向左移动)。由于浮动元素的状态不影响 span 并且它也没有自己的合成图层,所以它的不透明度不会被动画改变。
.blink_me:before {
content: "Blink";
float: left;
}
.blink_me {
-webkit-animation: blinker 1.5s linear infinite;
-moz-animation: blinker 1.5s linear infinite;
-o-animation: blinker 1.5s linear infinite;
animation: blinker 1.5s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0.0;
}
}
<span class="blink_me">Some content</span>
没有浮动:
在这种情况下,引擎创建两个图层和两个绘制矩形,但由于没有浮动,伪元素也是inline
的,并且是父级span
的一部分(您将看到一个框覆盖“Blink”和“Some content”)。 现在,由于伪元素的内容也是父元素的一部分,因此对父元素的动画也会影响伪元素的内容。
.blink_me:before {
content: "Blink";
}
.blink_me {
-webkit-animation: blinker 1.5s linear infinite;
-moz-animation: blinker 1.5s linear infinite;
-o-animation: blinker 1.5s linear infinite;
animation: blinker 1.5s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0.0;
}
}
<span class="blink_me">Some content</span>
解决方案:
以下任何一项操作都会导致伪元素的内容被视为父元素层的一部分,因此对父元素的动画也会影响子元素。当应用其中任何一个设置时,您会再次注意到橙色边框覆盖了span元素的内容和伪元素的内容。
Setting any position
on the pseudo-element (relative or absolute or even fixed).
.blink_me:before {
content: "Blink";
float: left;
position: relative;
}
.blink_me {
-webkit-animation: blinker 1.5s linear infinite;
-moz-animation: blinker 1.5s linear infinite;
-o-animation: blinker 1.5s linear infinite;
animation: blinker 1.5s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0.0;
}
}
<span class="blink_me">Some content</span>
Setting a opacity
other than 1 on the pseudo-element (like 0.99 etc).
.blink_me:before {
content: "Blink";
float: left;
opacity: 0.99;
}
.blink_me {
-webkit-animation: blinker 1.5s linear infinite;
-moz-animation: blinker 1.5s linear infinite;
-o-animation: blinker 1.5s linear infinite;
animation: blinker 1.5s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0.0;
}
}
<span class="blink_me">Some content</span>
Setting transform: translateZ(0px);
on the pseudo-element.
.blink_me:before {
content: "Blink";
float: left;
transform: translateZ(0px);
}
.blink_me {
-webkit-animation: blinker 1.5s linear infinite;
-moz-animation: blinker 1.5s linear infinite;
-o-animation: blinker 1.5s linear infinite;
animation: blinker 1.5s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0.0;
}
}
<span class="blink_me">Some content</span>
或者,另一种解决方案是直接在伪元素上设置动画,因为它会得到自己的合成层,只有该层会受到影响。
.blink_me:before {
content: "Blink";
float: left;
-webkit-animation: blinker 1.5s linear infinite;
-moz-animation: blinker 1.5s linear infinite;
-o-animation: blinker 1.5s linear infinite;
animation: blinker 1.5s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0.0;
}
}
<span class="blink_me"> </span>
另一个可行的选项是将父级
span
元素的
display
属性设置为
inline-block
或
block
。这样会使伪元素成为父元素的合成层的一部分,因此也会受到动画的影响。
.blink_me:before {
content: "Blink";
float: left;
}
.blink_me {
display: inline-block;
-webkit-animation: blinker 1.5s linear infinite;
-moz-animation: blinker 1.5s linear infinite;
-o-animation: blinker 1.5s linear infinite;
animation: blinker 1.5s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0.0;
}
}
<span class="blink_me"> </span>
总结
在参考资料中提供的第二个链接中,您将了解到WebKit(和Blink)中从Nodes到Render Objects到Render Layers到Compositing Layers的渲染过程是如何工作的。
以下是所有这些演示如何应用以及为什么它使它们能够按照它们的方式运行的摘要。
如果伪元素上没有浮动:
Element | Node | Render Object | Render Layer | Compositing Layer
-----------------------------------------------------------------------------------------
Root | Yes | Yes | Yes | Yes (Descendant is a compositing layer)
span | Yes | Yes | Yes | Yes
:before | Yes | Yes | No | N/A
当动画开始时,由于标签是半透明的(由于不透明度),所以它会立即获得呈现层,并且由于它具有不透明度动画,因此它会获得合成层。伪元素没有自己的呈现层,因为它不满足任何必需的条件,因此也不会获得合成层。它成为第一个祖先的呈现+合成层的一部分。在合成期间,伪元素的内容也会受到影响,因为它也是该层的一部分。
当伪元素上存在浮动时:
Element | Node | Render Object | Render Layer | Compositing Layer
-----------------------------------------------------------------------------------------
Root | Yes | Yes | Yes | Yes (Descendant is a compositing layer)
span | Yes | Yes | Yes | Yes
:before | Yes | Yes | No | N/A
和之前一样,但由于存在浮动并且它不是 span
的一部分,伪元素不属于它的组合层,因此在组合操作期间不会被修改。
当伪元素定位时:
Element | Node | Render Object | Render Layer | Compositing Layer
-----------------------------------------------------------------------------------------
Root | Yes | Yes | Yes | Yes (Descendant is a compositing layer)
span | Yes | Yes | Yes | Yes
:before | Yes | Yes | Yes | No
当伪元素被定位后,它会得到一个自己的呈现层(因为它符合条件),但是由于不符合所需的条件,它不会得到一个合成层。此外,它的定位意味着它在屏幕上的位置受到跨度上任何变换的影响。看起来这使得伪元素也成为跨度合成层的一部分,因此作为合成操作的一部分进行修改。
当伪元素的不透明度小于1时:
Element | Node | Render Object | Render Layer | Compositing Layer
-----------------------------------------------------------------------------------------
Root | Yes | Yes | Yes | Yes (Descendant is a compositing layer)
span | Yes | Yes | Yes | Yes
:before | Yes | Yes | Yes | No
和先前的情况类似。这里伪元素的不透明度小于1意味着它需要在它下面的图层发生变化时进行更改(否则,透明度会被破坏)。因此,由于这个原因,它似乎会移动到合成层并在合成过程中进行修改。
当伪元素有一个transform时:
Element | Node | Render Object | Render Layer | Compositing Layer
-----------------------------------------------------------------------------------------
Root | Yes | Yes | Yes | Yes (Descendant is a compositing layer)
span | Yes | Yes | Yes | Yes
:before | Yes | Yes | Yes | Yes
这里,伪元素也有自己的合成层,因为它有一个3D变换,并且它是的子元素,所以它的层在的层之上。这意味着在合成期间,和伪元素的层都会被修改,因此动画也会影响到这里的伪元素。
当伪元素直接进行动画时:
Element | Node | Render Object | Render Layer | Compositing Layer
-----------------------------------------------------------------------------------------
Root | Yes | Yes | Yes | Yes (Descendant is a compositing layer)
span | Yes | Yes | No | N/A
:before | Yes | Yes | Yes | Yes
在这里,span
没有渲染或合成层,因为不透明度动画位于伪元素上。由于伪元素有自己的合成层,在合成期间也会受到影响。
当display属性设置为块级元素或行内块级元素时:
Element | Node | Render Object | Render Layer | Compositing Layer
-----------------------------------------------------------------------------------------
Root | Yes | Yes | Yes | Yes (Descendant is a compositing layer)
span | Yes | Yes | Yes | Yes
:before | Yes | Yes | No | N/A
这与伪元素浮动类似,但由于此处的 span 是块级元素,它会为其后代和生成内容生成主块级框。因此,伪元素成为 span 的合成层的一部分,在合成期间受到影响。
注意:整个渲染过程非常复杂,你可以从参考链接中获得更多信息。我尽力解释了这个过程。虽然有可能我理解错了一些复杂的细节,但总体上,你会发现我的解释与 Dev 工具输出相符。
参考资料:
你可以通过以下链接了解如何启用“显示绘制矩形”、“显示合成层边框”选项以及加速渲染过程的工作方式: