如果元素开始隐藏,则 css 过渡不起作用

Ric*_*ard 5 javascript css jquery

我有 2div秒,其中之一是通过display:none;. 两者在属性上都有相同的 css 过渡right

如果我更改属性right通过jQuery和显示被隐藏的div,无论是使用$.css('display','none')$.show()$.toggle()等,隐藏DIV的结束位置瞬间画

$('button').on('click',function(){
  $('.b').show();
  $('.b').css('right','80%');
  $('.a').css('right','80%');
})
Run Code Online (Sandbox Code Playgroud)
body {
  width:800px;
  height:800px;
}

div {
  width:50px;
  height:50px;
  background-color:#333;
  position:absolute;
  display:none;
  right:5%;
  top:0;
  transition:right .5s cubic-bezier(0.645, 0.045, 0.355, 1);
  color: white;
}

.a {
  display:block;
  top:60px;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='a'>A
</div>
<div class='b'>B
</div>
<button>Launch</button>
Run Code Online (Sandbox Code Playgroud)

如果我使用$.animate()它会起作用。但我的问题是;这是错误还是正常行为?

编辑不是显示上的过渡的重复:属性导致这里的问题不是动画显示属性也不是可见性

Kai*_*ido 10

要想看清情况,就需要了解 CSSOM 和 DOM 的关系。

之前的 Q/A 中,我对重绘过程的工作原理有了一些了解。
基本上分为三个步骤,DOM 操作、回流和绘制。

  • 第一个(DOM 操作)只是修改一个 js 对象,并且都是同步的。
  • 第二个(回流,又名布局)是我们感兴趣的,并且稍微复杂一点,因为只有一些 DOM 方法和绘制操作需要它。它包括更新所有 CSS 规则并重新计算页面上每个元素的所有计算样式。
    作为一个相当复杂的操作,浏览器会尽量少做。
  • 第三个 ( paint ) 每秒最多只执行 60 次(仅在需要时)。

CSS 转换通过从一种状态转换到另一种状态来工作。为此,他们查看元素的最后计算值以创建初始状态。
由于浏览器仅在需要时才重新计算计算出的样式,因此在过渡开始时,您应用的所有 DOM 操作均无效。

所以在你的第一个场景中,当计算转换的初始状态时,我们有

.b { computedStyle: {display: none} }
Run Code Online (Sandbox Code Playgroud)

......就是这样。

因为,是的,这display: none就是 CSSOM 的强大之处;如果一个元素有display: none,那么它不需要被绘制,它不存在。

所以我什至不确定转换算法是否会启动,但即使它启动,初始状态对于任何可转换值也是无效的,因为所有计算值都为空。

你的.a元素可见从一开始就没有这个问题,并且可以转变。

如果您能够使其延迟(由 引起$.animate)工作,那是因为在确实更改display属性的 DOM 操作与触发转换的延迟 DOM 操作的执行之间,浏览器确实触发了回流(例如,因为屏幕垂直同步在两者之间启动并且绘制操作被触发)。


现在,这不是问题的一部分,但既然我们确实更了解发生了什么,我们也可以更好地控制它

事实上,一些 DOM 方法确实需要有最新的计算值。举例来说Element.getBoundingClientRect,或element.offsetHeightgetComputedStyle(element).height等等。所有这些需要整个页面已经更新,以便拳击正确做出的计算值(例如一个元素可以有一个保证金推或多或少等)。

这意味着我们不必知道浏览器何时会触发回流,我们可以强制它在我们想要的时候这样做。

但是请记住,页面上的所有元素都需要更新,这不是一个小操作,如果浏览器手头宽大,那是有充分理由的。

所以最好偶尔使用它,每帧最多使用一次。

幸运的是,Web API 使我们能够在此绘制操作发生之前挂钩一些 js 代码:requestAnimationFrame

所以最好的办法是在这个 pre-paint 回调中只强制我们重排一次,并从这个回调中调用所有需要更新值的东西。

.b { computedStyle: {display: none} }
Run Code Online (Sandbox Code Playgroud)
$('button').on('click',function(){
  $('.b').show(); // apply display:block synchronously
  
  requestAnimationFrame(() => { // wait just before the next paint
    document.body.offsetHeight; // force a reflow
    // trigger the transitions
    $('.b').css('right','80%');
    $('.a').css('right','80%');
  });
})
Run Code Online (Sandbox Code Playgroud)
body {
  width:800px;
  height:800px;
}

div {
  width:50px;
  height:50px;
  background-color:#333;
  position:absolute;
  display:none;
  right:5%;
  top:0;
  transition:right .5s cubic-bezier(0.645, 0.045, 0.355, 1);
  color: white;
}

.a {
  display:block;
  top:60px;
}
Run Code Online (Sandbox Code Playgroud)

但说实话,设置起来并不总是那么容易,所以如果你确定它会偶尔被触发,你可能会很懒惰并同步进行:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='a'>A
</div>
<div class='b'>B
</div>
<button>Launch</button>
Run Code Online (Sandbox Code Playgroud)
$('button').on('click',function(){
  $('.b').show(); // apply display:block
  document.body.offsetHeight; // force a reflow
  // trigger the transitions
  $('.b').css('right','80%');
  $('.a').css('right','80%');
})
Run Code Online (Sandbox Code Playgroud)
body {
  width:800px;
  height:800px;
}

div {
  width:50px;
  height:50px;
  background-color:#333;
  position:absolute;
  display:none;
  right:5%;
  top:0;
  transition:right .5s cubic-bezier(0.645, 0.045, 0.355, 1);
  color: white;
}

.a {
  display:block;
  top:60px;
}
Run Code Online (Sandbox Code Playgroud)