为什么 CSS 转换需要触发回流?

Why does reflow need to be triggered for CSS transitions?

我正在阅读这篇文章 http://semisignal.com/?p=5298 并且作者写道

"Reflow needs to be triggered before the invisible class is removed in order for the transition to work as expected. "

我的问题是:

1)为什么要触发reflow?

2) 我知道我们应该避免使用回流,如果这是真的,为什么作者建议使用回流以使转换工作正常?

3) 除了使用回流,是否有其他方法可以使转换工作正常?

谢谢。

(有效:"Why can't I easily use transitions with the display property")

简答:

CSS 转换依赖于元素的 startingstatic 属性。当元素设置为 display: none; 时,文档 (DOM) 将呈现为好像该元素不存在一样。这意味着当它设置为 display: block; 时 - 没有用于转换的起始值。

更长的答案

  1. 需要触发重排,因为设置为 display: none; 的元素尚未在文档中绘制。这可以防止转换具有起始 value/initial 状态。将元素设置为 display: none; 会使文档呈现为好像该元素根本不存在一样。
  2. 他建议重排,因为通常可以接受使用 display: none;display: block; 隐藏和显示元素 - 通常在元素已被某个操作(选项卡或按钮单击、回调函数、超时)请求之后功能等)。过渡是 巨大 用户体验的好处,因此回流是允许这些过渡发生的相对简单的方法。当您在简单网站上使用简单转换时,它不会产生巨大影响,因此对于一般用途,您可以 触发重排,即使从技术上讲您不应该这样做。想想这个人的例子,比如在生产站点中使用未缩小的 JavaScript 文件。 可以吗?当然! 你应该吗?可能不会,但在大多数情况下,它不会产生明显的差异。
  3. 有不同的选项可以防止回流,或者通常比您提供的 link 中的方法更易于使用。以下面的片段为例:

A:这个元素设置为height: 0;overflow: hidden;。显示时,它设置为 height: auto;。我们仅将动画应用于 opacity。这给了我们类似的效果,但我们可以在没有回流的情况下转换它,因为它已经在文档中呈现并为转换提供初始值以供使用。

B:该元素与A相同,但将高度设置为定义的大小。

A 和 B 在元素淡入淡出方面 工作得很好 ,但是因为我们立即将高度从 auto/100px 设置为 0,所以它们似乎崩溃了在 "fade out"

C:这个元素是隐藏的,我们尝试转换child。你可以看到这也不起作用,需要触发回流。

D:这个元素是隐藏的,我们动画 child。由于动画关键帧给出了定义的起始值和结束值,因此效果更好。但是请注意,黑框会快速进入视图,因为它仍然附加到 parent.

E:这与 D 的工作方式类似,但我们 运行 关闭 child 的所有内容,这并没有解决我们的 "black box" 问题我们和D.

F:这可能是两全其美的解决方案。我们将样式从 parent 移到 child 上。我们可以触发 parent 的动画,我们可以根据需要控制 child 的 display 属性 和 animate 的过渡。这样做的缺点是您需要使用动画关键帧而不是过渡。

G:虽然我不知道这是否会触发函数内部的回流,因为我自己还没有解析过它,你可以简单地使用 jQuery的 .fadeToggle() 函数用一行 JavaScript 来完成所有这些,并且被经常使用(或类似的 JS/jQuery fadeIn/fadeOut 方法)以至于重排的主题没有经常出现。

示例:

这是一个 CodePen:https://codepen.io/xhynk/pen/gerPKq

这是一个片段:

jQuery(document).ready(function($){
  $('button:not(#g)').click(function(){
   $(this).next('div').toggleClass('show');
  });

  $('#g').click(function(){
   $(this).next('div').stop().fadeToggle(2000);
  });
});
* { box-sizing: border-box; }

button {
 text-align: center;
 width: 400px;
}

div {
 margin-top: 20px;
 background: #000;
 color: #fff;
}

.a,
.b {
 overflow: hidden;
 height: 0;
 opacity: 0;
 transition: opacity 3s;
}

.a.show {
 height: auto;
 opacity: 1;
}
.b.show {
 height: 100px;
 opacity: 1;
}

.c,
.d {
 display: none;
}

.c.show,
.d.show {
 display: block; 
}

.c div {
 opacity: 0;
 transition: 3s all;
}

.c.show div {
 opacity: 1;
}

.d div {
 opacity: 0;
}

.d.show div {
 animation: fade 3s;
}

@keyframes fade {
    from { opacity: 0; }
      to { opacity: 1; }
}

.e div {
 display: none;
}

.e.show div {
 display: block;
 animation: fade 3s;
}

.f {
 background: transparent;
}

.f div {
 background: #000;
 display: none;
}

.f.show div {
 display: block;
 animation: fade 3s;
}


.g {
 display: none;
}
<button id="a">A: Box Height: Auto</button>
<div class="a">This<br/>Has<br/>Some Strange<br/><br/>Content<br>But<br>That doesn't really<br>Matter<br/>Because shown,<br/>I'll be<br/>AUTO</div>
<button id="b">B: Box Height: 100px</button>
<div class="b">Content For 2</div>
<button id="c">C: Hidden - Child Transitions (bad)</button>
<div class="c"><div>Content<br/>For<br/>3<br/></div></div>
<div style="clear: both;"></div>
<button id="d">D: Hidden - Child Animates (Better)</button>
<div class="d"><div>Content<br/>For<br/>4<br/></div></div>
<div style="clear: both;"></div>
<button id="e">E: Hidden - Child Hidden & Animates</button>
<div class="e"><div>Content<br/>For<br/>5<br/></div></div>
<button id="f">F: Child Has BG & Animates (Works)</button>
<div class="f"><div>Content<br/>For<br/>5<br/></div></div>
<div style="clear: both;"></div>
<button id="g">G: This uses fadeToggle to avoid this</button>
<div class="g">I animate with<br/>JavaScript</div>
<footer>I'm just the footer to show the bottom of the document.</footer>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>