javascript - 使用过渡 + 不透明度更改 + 溢出隐藏时出现问题

标签 javascript html css css-animations

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

我想删除 div 之外的内容。溢出无法正常工作。 (删除 transition 有效,但如果可能,我想保留它)

任何帮助表示赞赏

Codepen Link

代码

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 Rocks - Accelerated Rendering in Chrome
  • GPU Accelerated Compositing in Chrome .


  • 为什么禁用过渡时不会发生此问题?
  • 当样式改变但没有满足创建合成层的必要条件时(即,没有动画、过渡或 3D 变换等):
  • 没有合成层,因此整个区域似乎在每次更改时都会重新绘制。由于发生了完全重绘,因此没有问题。
  • 从开发工具启用“显示绘制矩形”和“显示合成图层边框”后查看以下代码段(在全屏模式下)并观察以下内容:
  • 不会创建带有橙色边框(合成层)的区域。
  • 每次通过将焦点设置在 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 会将它们拆分为不同的合成层。请注意当焦点设置在其中一个 anchor 标记上时,两个带有橙色边框的区域是如何显示的。这些是创建的合成层。
  • 层拆分正在发生以加速渲染。正如 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在 parent 身上也得到应用和尊重。然而,在过渡期间,只有合成层的属性发生了变化,因此该层似乎不知道其他层的属性,因此不尊重父层的边界半径。

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

    评论在 this bug thread似乎也确认当不再需要单独的图层时会发生重绘。

    We want to repaint if "gets own layer" is going to change



    注:尽管它们是特定于 Chrome 的,但我认为其他人的行为也应该类似。

    解决办法是什么?

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

    正如 BoltClock 在 this answer 中提到的,以下任何一个选项都会为父级创建一个单独的堆叠上下文,并且执行其中之一似乎可以解决问题。
  • 设置 z-index在父级 .qs-timer除了汽车以外的任何东西。

    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 转换的浏览器。

    关于javascript - 使用过渡 + 不透明度更改 + 溢出隐藏时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31693219/

    相关文章:

    c# - 从javascript函数传递提示框值- PostBack to c#

    javascript - 是否可以从右向左显示div的内容

    javascript - Bootstrap 如何通过 DOM 更改通知?

    javascript - 如何从所有已检查的表行中获取所有 id?

    html - 在现有代码中使用 CSS/HTML 添加移动/淡入淡出过渡或效果

    html - 网站元素在较小的屏幕尺寸下不继承颜色

    javascript - 当我随机化银行变量时,bankMoney 变为 NaN

    javascript - Javascript 搜索函数中的错误处理

    html - 网页未显示所有 HTML 元素

    html - 如何管理下拉列表作为移动设备的链接