问题,而使用过渡+不透明度变化+溢出隐藏

Issue while using transitions + opacity change + overflow hidden

本文关键字:不透明度 变化 溢出 隐藏 问题      更新时间:2023-09-26

如果您看到我分享的代码示例,您可以看到覆盖在框外。我将问题追溯到transition属性。

我想删除div外的内容。溢出不工作,因为它应该。(删除transition工作,但我想保留它,如果可能的话)

感谢您的帮助

Codepen联系

var timer = setInterval(function() {
  document.querySelector(".qs-timer-overlay").style.opacity = (document.querySelector(".qs-timer-overlay").style.opacity * 1) + 0.1;
  if (document.querySelector(".qs-timer-overlay").style.opacity * 1 == 1) {
    clearInterval(timer);
  }
}, 1000);
.qs-main-header .qs-timer {
  padding: 13px 10px;
  min-width: 130px;
  text-align: center;
  display: inline-block;
  background-color: #dd8b3a;
  color: #FFF;
  font-size: 20px;
  border-radius: 50px;
  text-transform: uppercase;
  float: right;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.qs-main-header .qs-timer-overlay {
  z-index: 1;
  width: 10%;
  max-width: 100%;
  position: absolute;
  height: 100%;
  top: 0;
  left: 0;
  background-color: #c7543e;
  opacity: 0.0;
  /* border-radius: 50px 50px 0px 50px; */
}
.qs-main-header .qs-timer-content {
  z-index: 2;
  position: relative;
}
.scale-transition {
  -webkit-transition: all 1s;
  transition: all 1s;
}
<div class="qs-main-header">
  <div class="qs-timer scale-transition ng-hide" ng-show="visibility.timer">
    <div class="scale-transition qs-timer-overlay"></div>
    <div class="qs-timer-content ng-binding">0 <span class="ng-binding">Sec(s)</span>
    </div>
  </div>
</div>

实际上是border-radius在过渡发生时没有得到尊重。这是因为创建了加速渲染的合成层,可以通过查看以下文章来解释:

  • HTML5岩石- Chrome加速渲染
  • GPU加速合成在Chrome。

为什么当转换被禁用时不会发生这个问题?

  • 当样式改变,但没有一个需要创建合成层的标准被满足(即,没有动画或过渡或3D变换等):
    • 没有合成层,所以整个区域似乎在每次变化时都被重新绘制。因为一个完整的重漆发生,没有问题。
  • 在Dev工具中启用"显示绘制矩形"answers"显示合成层边界"后,查看下面的代码片段(全屏模式),观察如下:
    • 没有创建橙色边框(合成层)的区域。
    • 每次通过将焦点设置在a标签上修改样式时,整个区域都会被重新绘制(红色或绿色闪烁区域)。

.outer {
  position: relative;
  height: 100px;
  width: 100px;
  margin-top: 50px;
  border: 1px solid red;
  overflow: hidden;
}
.border-radius {
  border-radius: 50px;
}
.inner {
  width: 50px;
  height: 50px;
  background-color: gray;
  opacity: 0.75;
}
a:focus + .outer.border-radius > .inner {
  transform: translateX(50px);
  height: 51px;
  opacity: 0.5;
}
<a href='#'>Test</a>
<div class='outer border-radius'>
  <div class='inner'>I am a strange root.
  </div>
</div>


为什么添加过渡会产生问题?

  • 初始渲染没有合成层,因为元素上还没有过渡。查看下面的代码片段,并注意当代码片段运行时,油漆(红色或绿色闪烁区域)发生了,但没有创建合成层(橙色边框区域)。
  • 当过渡开始时,Chrome将它们分成不同的合成层,当一些属性,如不透明度,转换等正在过渡。注意,一旦将焦点设置在其中一个锚标记上,就会显示两个带有橙色边框的区域。这些是创建的合成层。
  • 层分裂是为了加速渲染。正如HTML5 Rocks文章中提到的,不透明度和变换的改变是通过改变合成层的属性来实现的,不会发生重绘。
  • 在过渡结束时,重新绘制恰好将所有图层合并回单个图层,因为合成图层不再适用(基于创建图层的标准)。

.outer {
  position: relative;
  height: 100px;
  width: 100px;
  margin-top: 50px;
  border: 1px solid red;
  overflow: hidden;
}
.border-radius {
  border-radius: 50px;
}
.inner {
  width: 50px;
  height: 50px;
  background-color: gray;
  transition: all 1s 5s;
  /*transition: height 1s 5s; /* uncomment this to see how other properties don't create a compositing layer */
  opacity: 0.75;
}
a:focus + .outer.border-radius > .inner {
  transform: translateX(50px);
  opacity: 0.5;
  /*height: 60px; */
}
<a href='#'>Test</a>
<div class='outer border-radius'>
  <div class='inner'>I am a strange root.
  </div>
</div>

这说明,当图层合并回来和完全重绘发生时,父层上的border-radius也得到应用和尊重。然而,在过渡期间,只有合成层的属性被改变,因此该层似乎不知道其他层的属性,因此不尊重父层的边界半径。

我认为这是因为图层渲染的方式。每一层都是一个软件位图,所以它有点相当于有一个圆形图像,然后在它上面放一个div。这显然不会导致任何内容的剪辑。

这个bug线程中的注释似乎也证实了当不再需要单独的层时,会发生重绘。

如果"gets own layer"要改变,我们想要重新绘制

注意:虽然它们是Chrome特定的,我认为行为应该是类似的在其他也。


解决方案是什么?

解决方案似乎是为父元素(.qs-timer)创建一个单独的堆叠上下文。创建一个单独的堆叠上下文似乎会导致为父层创建一个单独的合成层,这解决了这个问题。

正如BoltClock在这个回答中提到的,以下任何一个选项都会为父节点创建一个单独的堆栈上下文,并且执行其中一个选项似乎可以解决这个问题。

  • 将父.qs-timer上的z-index设置为非auto。

    var timer = setInterval(function() {
      document.querySelector(".qs-timer-overlay").style.opacity = (document.querySelector(".qs-timer-overlay").style.opacity * 1) + 0.1;
      if (document.querySelector(".qs-timer-overlay").style.opacity * 1 == 1) {
        clearInterval(timer);
      }
    }, 1000);
    .qs-main-header .qs-timer {
      padding: 13px 10px;
      min-width: 130px;
      text-align: center;
      display: inline-block;
      background-color: #dd8b3a;
      color: #FFF;
      font-size: 20px;
      border-radius: 50px;
      text-transform: uppercase;
      float: right;
      cursor: pointer;
      position: relative;
      overflow: hidden;
      z-index: 1; /* creates a separate stacking context */
    }
    .qs-main-header .qs-timer-overlay {
      z-index: 1;
      width: 10%;
      max-width: 100%;
      position: absolute;
      height: 100%;
      top: 0;
      left: 0;
      background-color: #c7543e;
      opacity: 0.0;
      /* border-radius: 50px 50px 0px 50px; */
    }
    .qs-main-header .qs-timer-content {
      z-index: 2;
      position: relative;
    }
    .scale-transition {
      -webkit-transition: all 1s;
      transition: all 1s;
    }
    <div class="qs-main-header">
      <div class="qs-timer scale-transition ng-hide" ng-show="visibility.timer">
        <div class="scale-transition qs-timer-overlay"></div>
        <div class="qs-timer-content ng-binding">0 <span class="ng-binding">Sec(s)</span>
        </div>
      </div>
    </div>

  • 设置opacity为小于1的值。我在下面的代码片段中使用了0.99,因为它不会造成任何视觉差异。

    var timer = setInterval(function() {
      document.querySelector(".qs-timer-overlay").style.opacity = (document.querySelector(".qs-timer-overlay").style.opacity * 1) + 0.1;
      if (document.querySelector(".qs-timer-overlay").style.opacity * 1 == 1) {
        clearInterval(timer);
      }
    }, 1000);
    .qs-main-header .qs-timer {
      padding: 13px 10px;
      min-width: 130px;
      text-align: center;
      display: inline-block;
      background-color: #dd8b3a;
      color: #FFF;
      font-size: 20px;
      border-radius: 50px;
      text-transform: uppercase;
      float: right;
      cursor: pointer;
      position: relative;
      overflow: hidden;
      opacity: 0.99; /* creates a separate stacking context */
    }
    .qs-main-header .qs-timer-overlay {
      z-index: 1;
      width: 10%;
      max-width: 100%;
      position: absolute;
      height: 100%;
      top: 0;
      left: 0;
      background-color: #c7543e;
      opacity: 0.0;
      /* border-radius: 50px 50px 0px 50px; */
    }
    .qs-main-header .qs-timer-content {
      z-index: 2;
      position: relative;
    }
    .scale-transition {
      -webkit-transition: all 1s;
      transition: all 1s;
    }
    <div class="qs-main-header">
      <div class="qs-timer scale-transition ng-hide" ng-show="visibility.timer">
        <div class="scale-transition qs-timer-overlay"></div>
        <div class="qs-timer-content ng-binding">0 <span class="ng-binding">Sec(s)</span>
        </div>
      </div>
    </div>

  • 为元素添加transform。我在下面的代码片段中使用了translateZ(0px),因为这也不会产生任何视觉差异。

    var timer = setInterval(function() {
      document.querySelector(".qs-timer-overlay").style.opacity = (document.querySelector(".qs-timer-overlay").style.opacity * 1) + 0.1;
      if (document.querySelector(".qs-timer-overlay").style.opacity * 1 == 1) {
        clearInterval(timer);
      }
    }, 1000);
    .qs-main-header .qs-timer {
      padding: 13px 10px;
      min-width: 130px;
      text-align: center;
      display: inline-block;
      background-color: #dd8b3a;
      color: #FFF;
      font-size: 20px;
      border-radius: 50px;
      text-transform: uppercase;
      float: right;
      cursor: pointer;
      position: relative;
      overflow: hidden;
      transform: translateZ(0px) /* creates a separate stacking context */
    }
    .qs-main-header .qs-timer-overlay {
      z-index: 1;
      width: 10%;
      max-width: 100%;
      position: absolute;
      height: 100%;
      top: 0;
      left: 0;
      background-color: #c7543e;
      opacity: 0.0;
      /* border-radius: 50px 50px 0px 50px; */
    }
    .qs-main-header .qs-timer-content {
      z-index: 2;
      position: relative;
    }
    .scale-transition {
      -webkit-transition: all 1s;
      transition: all 1s;
    }
    <div class="qs-main-header">
      <div class="qs-timer scale-transition ng-hide" ng-show="visibility.timer">
        <div class="scale-transition qs-timer-overlay"></div>
        <div class="qs-timer-content ng-binding">0 <span class="ng-binding">Sec(s)</span>
        </div>
      </div>
    </div>

前两种方法比第三种方法更可取,因为第三种方法只适用于支持CSS转换的浏览器。

是的,将opacity: 0.99;添加到.qs-timer的问题将会修复。

当不透明度:1或NOT定义:
在这种特殊情况下,没有涉及透明度,因此gfx可以避免做昂贵的事情。

In case: 0.99:
nsIFrame::HasOpacity()决定有一个不透明度,所以gfx包含有价值的东西。(像opacityborder-radius一样)

需要更多的帮助,特殊情况下不透明度:0.99,将其视为不透明度:1的图形,这张票并没有提供我们的实际目标的意见,但给出了关于CSS内部发生的事情的想法。