CSS3动画在WebKit浏览器中无法正常工作,如果div:before浮动。

14

我想要使用CSS3使我的文本闪烁。它可以正常工作。但是,如果我在div的:before选择器中添加float:left,它会阻止动画在WebKit(Safari/Chrome)上工作。

为了演示,打开JSFiddle并在WebKit上移除float:left以查看它运行。

CSS:

.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; }
}

HTML:

<span class="blink_me"> </span>

如何使选择器中的浮动生效?

悬赏信息:奖励一个已存在的回答,该回答确实值得更多点赞。


2
我可以确认它在FF上运行正常,但在webkit上不行。有趣的问题。+1 - Gary Woods
1
在伪选择器中添加position:relative。不幸的是,我无法提供更多信息:D - Tommy
@Tonsenson 这并不是一个恰当的回答。如果您无法提供更多的信息,何必浪费时间进行评论呢? :) - Henrik Petterson
只是因为我不明白这个如何解决问题,也许有人读了我的评论可以解释一下并帮助我们理解 :) - Tommy
1个回答

15

原因:

这似乎是由于 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-blockblock。这样会使伪元素成为父元素的合成层的一部分,因此也会受到动画的影响。

.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 工具输出相符。


参考资料:

你可以通过以下链接了解如何启用“显示绘制矩形”、“显示合成层边框”选项以及加速渲染过程的工作方式:


1
非常感谢你,我的朋友。虽然它没有直接回答问题,但确实提供了一个真正的解决方案。稍后会接受。 - Henrik Petterson
@HenrikPetterson:是的,我很想给你一个直接的答案(我正在尝试)。不要接受答案,至少在几个小时内不要接受,因为已有被接受答案的问题会得到较少的浏览量,可能会让潜在的回答者望而却步。 - Harry
1
是的,@HenrikPetterson可能与此相关,但我发现这个问题不仅发生在伪元素上。即使我们将子元素浮动到显示为内联的父元素中,它也会发生。更有趣的是,子/伪元素上的position: relative也可以解决它(正如在问题的评论中指出的那样)。 - Harry
3
浮动的:before是一个独立的盒子。如果你检查具有浮动的元素,:before是一个盒子,而span是一个独立的盒子!动画无法影响浮动的:before,但其他属性如color可以生效。这很奇怪。小示例:http://jsfiddle.net/umz8t/5304/ - Sebastian Brosch
1
不,我只是在等这个问题有赏金,这样我就可以为你的杰出答案奖励你。谢谢一切! - Henrik Petterson
哦,我明白了。非常感谢你,伙计。@HenrikPetterson 我的回答还有一些小问题需要解决。希望赏金能吸引更多人,也许你会得到一个更完整的答案 :) - Harry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接