为什么需要为CSS转换触发重排?

时间:2018-03-14 04:06:00

标签: javascript css performance reflow

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

"在删除不可见类之前需要触发重排以使转换按预期工作。 "

我的问题是:

1)为什么需要触发回流?

2)我明白我们应该避免使用回流,如果这是真的,为什么作者建议使用回流来使转换工作?

3)有没有一种不同的方法可以使转换工作,而不是使用回流?

谢谢。

1 个答案:

答案 0 :(得分:2)

(实际上:“我为什么不能轻易使用display属性的转换”)

简答

CSS Transitions依赖于元素的启动静态属性。当元素设置为display: none;时,文档(DOM)将呈现为元素不存在。这意味着当它设置为display: block;时 - 没有转换的起始值。

更长的答案

  1. 需要触发回流,因为尚未在文档中绘制设置为display: none;的元素。这可以防止转换具有起始值/初始状态。将元素设置为display: none;会使文档呈现,就像元素根本不存在一样。
  2. 他建议回流,因为通常会接受隐藏和显示display: none;display: block;的元素 - 通常是在操作请求元素之后(选项卡或按钮单击,回调函数,超时功能,等等。)。转换是UX的巨大奖励,因此回流是允许这些转换发生的相对简单的方法。当您在简单网站上使用简单转换时,它不会产生巨大影响,因此出于一般目的,您可以触发重排,即使技术上您不应该这样做。想想这个人的例子就像在生产站点中使用未经编辑的JavaScript文件一样。 你能吗?当然! 你应该吗?可能不是,但在大多数情况下,它不会产生明显的差异。
  3. 有不同的选项可以阻止回流,或者通常比您提供的链接中的方法更容易使用。请参考以下代码段以获取一些示例:
  4. A :此元素设置为height: 0;overflow: hidden;。显示时,它设置为height: auto;。我们仅将动画应用于opacity。这给了我们类似的效果,但是我们可以在没有重排的情况下转换它,因为它已经在文档中呈现并且提供了转换初始值以供使用。

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

    A和B工作足够用于淡化元素,但由于我们立即将高度从auto/100px设置为0,它们似乎在“淡出”时崩溃

    C :此元素已隐藏,我们尝试转换子元素。您可以看到这也不起作用,需要触发重排。

    D :此元素已隐藏,我们动画该子元素。由于动画关键帧给出了定义的起始值和结束值,因此效果更好。但请注意,黑框会捕捉到视图,因为它仍然附加到父级。

    E :这与D的工作方式类似,但我们会关闭孩子的所有内容,但这并不能解决我们与D的“黑匣子”问题。

    F :这可能是两全其美的解决方案。我们将父母的样式移到孩子身上。我们可以触发父项的动画,我们可以根据需要控制子项的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>