【问题标题】:CSS Animation: `backface-visibility` causing cross-browser problems?CSS 动画:`backface-visibility` 导致跨浏览器问题?
【发布时间】:2020-10-14 02:04:42
【问题描述】:

我正在开发一个翻转动画来显示新数字;它很像一个中间有铰链的模拟时钟或日历。

方法很简单:有一个div 和:

  • 一侧第一个数字的下半部分
  • 第二个数字的上半部分旋转了 180 度,因此位于背面

为了显示新数字,我将整个 div 围绕容器中心旋转,露出旋转 div 的背面: Number flip animation in latest Firefox

但是,在 Chrome 中,动画并不总是有效。有时一半会完全消失,直到过渡动画完成,有时旧数字不会呈现:Number flip animation in latest Chrome with the bottom of the number not appearing till after animation is complete

在 Safari 12 中,更糟糕的是,它似乎不尊重 backface-visibility,即使带有 -webkit- 前缀: Safari 12 Number animation, the bottom half of the first number is inverted after animation is complete

Pre-Chromium Edge 可以很好地处理这个问题,但新的(在 v83 中检查)Edge 与 Chrome 存在相同的问题。

我尝试过处理这些属性,并在此处查看了其他 backface-visibility 问题。

这是代码,将鼠标悬停在数字上即可查看翻转:

body {
  background: #2e517d;
}

.container {
  width: 175px;
  height: 192px;
  background: #4e9bfa;
  position: relative;
  left: 50%;
  transform: translate(-50%, 50%);
  perspective: 1000px;
}

.cover {
  width: 175px;
  height: 50%;
  position: absolute;
  top: 96px;
  background-color: #34b58c;
  transform: rotateX(0deg);
  transform-style: preserve-3d;
  transform-origin: top;
  transition: all 0.5s ease-out;
}

.container:hover .cover {
  transform: rotateX(180deg);
}

.flip {
  margin: 0;
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
}

.container p {
  font-size: 1000%;
  margin: 0;
}

.container>p {
  height: 96px;
  overflow: hidden;
}

.front-number-bottom {
  position: relative;
  height: 96px;
  overflow: hidden;
  background-color: red;
}

.front-number-bottom p {
  margin: 0;
  position: relative;
  top: -96px;
}

.back-number-top {
  position: relative;
  height: 96px;
  overflow: hidden;
}

.back-number-bottom {
  height: 96px;
  overflow: hidden;
  position: relative;
  z-index: -1;
}

.back-number-bottom p {
  margin: 0;
  position: relative;
  top: -96px;
}

div.front {
  background: red;
}

div.back {
  background: green;
  transform: rotateX(180deg);
}
<body>
  <div class="container">
    <p>76</p>
    <div id="cover" class="cover">
      <div class="flip front">
        <div class="front-number-bottom">
          <p>76</p>
        </div>
      </div>
      <div class="flip back">
        <div class="back-number-top">
          <p>77</p>
        </div>
      </div>
    </div>
    <div class="back-number-bottom">
      <p>77</p>
    </div>
  </div>
  </div>
</body>

这是一种可以在 Chromium 浏览器和 Safari 中轻松修复的合理方法吗?

不同的方法会更好吗?

【问题讨论】:

  • 你试过z-index吗?
  • @Leo,您如何设想使用z-index?我没有尝试在动画期间对其进行操作。
  • 做类似的事情 (this),我了解到当旋转元素会为内容渲染产生不可预测的行为时,特别是对于它的 z-index。在这些情况下,我们应该手动设置该索引,而不是让它随时呈现。

标签: html css cross-browser css-animations


【解决方案1】:

我猜你的代码有点复杂。我会简化你的逻辑,如下所示,你不再需要backface-visibility: hidden;

注意两个重要事项的用法:

  • 允许我剪切元素并仅显示 50% 高度(顶部或底部)的蒙版。这将使动画更加逼真,因为每个数字的顶部和底部都是分开的。
  • z-index 技巧,我在动画中间应用了一个转换来更改 z-index(当旋转位于 90deg 时)1

.card {
  width: 175px;
  height: 192px;
  position: relative;
  z-index:0;
  left: 50%;
  transform: translate(-50%, 50%);
  font-size: 160px;
}
.card span,
.card span::before,
.card span::after {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
}
.card span {
  position:absolute;
  z-index:2;
  perspective: 1000px;
}
.card span:first-child {
  z-index:3;
  transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
  content:attr(data-number);
  -webkit-mask:linear-gradient(#fff,#fff) top/100% 50% no-repeat;
          mask:linear-gradient(#fff,#fff) top/100% 50% no-repeat;
  background:red;
  transition:0.5s all linear;
  transform-style: preserve-3d;
}
.card span::after {
  -webkit-mask-position:bottom;
          mask-position:bottom;
  background:green;
}

.card span:first-child::after {
    transform: rotateX(0deg);
}
.card span:last-child::before {
    transform: rotateX(-180deg);
}

/* Hover */
.card:hover span:first-child {
  z-index:1;
}
.card:hover span:first-child::after {
    transform: rotateX(180deg);
}
.card:hover span:last-child::before {
    transform: rotateX(0deg);
}
<div class="card">
  <span data-number="76"></span>
  <span data-number="77"></span>
</div>

遮罩也可以替换为clip-path:

.card {
  width: 175px;
  height: 192px;
  position: relative;
  z-index:0;
  left: 50%;
  transform: translate(-50%, 50%);
  font-size: 160px;
}
.card span,
.card span::before,
.card span::after {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
}
.card span {
  z-index:2;
  perspective: 1000px;
}
.card span:first-child {
  z-index:3;
  transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
  content:attr(data-number);
  clip-path:polygon(0 0,100% 0,100% 50%,0 50%);
  background:red;
  transition:0.5s all linear;
  transform-style: preserve-3d;
}
.card span::after {
  clip-path:polygon(0 50%,100% 50%,100% 100%,0 100%);
  background:green;
}

.card span:first-child::after {
    transform: rotateX(0deg);
}
.card span:last-child::before {
    transform: rotateX(-180deg);
}

/* Hover */
.card:hover span:first-child {
  z-index:1;
}
.card:hover span:first-child::after {
    transform: rotateX(180deg);
}
.card:hover span:last-child::before {
    transform: rotateX(0deg);
}
<div class="card">
  <span data-number="76"></span>
  <span data-number="77"></span>
</div>

另一个使用counter 的优化,没有设置明确的宽度/高度

.card {
  margin:0 5px;
  font-family:monospace;
  display:inline-block;
  text-align:center;
  position: relative;
  z-index:0;
  font-size: 150px;
  counter-reset:num calc(var(--n,1) - 1);
}
/* this will defined the height/width*/
.card::after {
  content:counter(num);
  visibility:hidden;
}
/**/
.card span,
.card span::before,
.card span::after {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
}
.card span {
  z-index:2;
  perspective: 1000px;
  counter-increment:num;
}
.card span:first-child {
  z-index:3;
  transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
  content:counter(num);
  clip-path:polygon(0 0,100% 0,100% 50%,0 50%);
  background:red;
  transition:0.5s all linear;
  transform-style: preserve-3d;
}
.card span::after {
  clip-path:polygon(0 50%,100% 50%,100% 100%,0 100%);
  background:green;
}

.card span:first-child::after,
.card:hover span:last-child::before{
    transform: rotateX(0deg);
}
.card span:last-child::before {
    transform: rotateX(-180deg);
}
.card:hover span:first-child::after {
    transform: rotateX(180deg);
}
.card:hover span:first-child {
  z-index:1;
}
<div class="card" style="--n:75">
  <span></span><span></span>
</div>

<div class="card" style="--n:5">
  <span></span><span></span>
</div>

<div class="card" style="--n:100">
  <span></span><span></span>
</div>

1 当使用linear 时,它非常简单,但使用其他轻松函数时会更巧妙。这是一个相关问题,可以帮助您识别缓动函数的中间部分:When exactly does an ease animation reach its midpoint?

【讨论】:

  • 如果我将转换计时功能更改为线性以外的其他功能(例如缓出),这些会闪烁。最好的解决方法是什么?
  • @NicholasWeber 读到:stackoverflow.com/a/62474609/8620333 您需要确保 z-index 恰好在动画中间更新,因此您需要找到它在中间的时间。对于线性来说,这很容易,因为它是 50%,但对于简单来说,它就不同了
猜你喜欢
  • 2016-10-30
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 2014-03-03
  • 2017-01-19
相关资源
最近更新 更多