原因:
这肯定是因为 Webkit 中的层创建和加速渲染过程。在开发工具中启用“显示绘制矩形”和“显示复合层边框”选项后,查看此答案中的所有演示。
运行任何一个演示时,您都会看到一些绿色和橙色框。绿色框是绘制矩形,而橙色框是渲染引擎为加速渲染而创建的合成层。在渲染过程中,Webkit(和 Blink)并不总是重新绘制整个页面。只有页面的受影响区域(层)被重新绘制(为了性能)。
带浮点数:
在这个 sn-p 中,您会看到渲染引擎创建了一个绘制矩形、一个页面合成层和一个绘制矩形、span 元素(“某些内容”)的合成层。由于span 是inline 元素,它不会生成包含其后代框和生成内容的principal block-level box。这(根据我的理解)使伪元素相对于根元素浮动。这也意味着伪元素在屏幕上的位置不依赖于父 span 元素(事实上,如果你给 span 一个负边距,你会注意到内容重叠,而如果为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_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 元素内容和伪元素内容的橙色边框。
-
在伪元素(相对或绝对甚至固定)上设置任何position。
.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>
-
在伪元素上设置除 1 以外的 opacity(如 0.99 等)。
.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>
-
在伪元素上设置transform: translateZ(0px);。
.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>
另一个可行的选项是将display 设置为inline-block 或block 到父span。这也使得伪元素成为父元素合成层的一部分,因此它也受到动画的影响。
.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>
总结
在 References 下提供的第二个链接中,您将看到渲染过程在 WebKit(和 Blink)中是如何工作的,从节点到渲染对象到渲染层再到合成层。
以下是这些如何应用于此答案中所有演示的摘要,以及为什么它使每个演示都以它们的方式工作。
当伪元素上没有浮动时:
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 在动画开始时立即获得一个渲染层,因为它是半透明的(由于不透明),并且它获得了一个合成层,因为它具有不透明动画。伪类没有自己的渲染层,因为它不满足任何要求的标准,因此也没有合成层。它成为第一个祖先的渲染+合成层的一部分。在合成过程中,pseudo 的内容也会受到影响,因为它也是 layer 的一部分。
当伪元素上有浮动时:
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
当 psuedo 被定位时,它会获得一个自己的渲染层(因为它符合标准),但没有获得合成层,因为它不符合所需的标准。此外,它的定位意味着它在屏幕上的位置会受到跨度上的任何变换的影响。看起来这使得伪元素也成为 span 合成层的一部分,因此被修改为合成操作的一部分。
当伪元素的不透明度小于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的伪意味着当它下面的层发生变化时它需要改变(否则透明度会被破坏)。因此,这似乎被移到了合成层,因此在合成过程中被修改了。
当伪元素有变换时:
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
这里,pseudo 也有自己的合成层,因为它上面有一个 3D 变换,并且因为它是 span 的子级,所以它的层在 span 的层之上。这意味着在合成过程中,span 和伪元素的图层都会被修改,因此动画也会影响这里的伪。
伪元素直接动画时:
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 没有渲染或合成层,因为不透明动画位于伪元素上。由于 pseudo 有自己的合成层,所以它在合成过程中也会受到影响。
当 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 | No | N/A
这与伪元素浮动的情况非常相似,但由于此处的 span 是块级元素,因此它会为其后代生成主要块级框并生成内容。因此,pseudo 成为 span 合成层的一部分,因此在合成过程中受到影响。
注意:整个渲染过程非常复杂,您可以从参考链接中看到,我已尽力解释该过程。我可能有一些错综复杂的细节是错误的,但总的来说,你会发现解释与开发工具的输出相符。
参考资料:
您可以通过以下链接找到有关如何启用“显示绘制矩形”、“显示复合图层边框”选项以及加速渲染过程如何工作的更多信息: