要想看清情况,就需要了解CSSOM和DOM的关系。
在之前的 Q/A 中,我对重绘过程的工作原理有了一些了解。
基本上分为三个步骤,DOM 操作、回流和绘制。
- 第一个(DOM 操作)只是修改一个 js 对象,并且都是同步的。
- 第二个(回流,又名布局)是我们感兴趣的,并且稍微复杂一些,因为只有一些 DOM 方法和绘制操作需要它。它包括更新所有 CSS 规则并重新计算页面上每个元素的所有计算样式。
作为一个相当复杂的操作,浏览器会尽量少做。
- 第三个 ( paint ) 每秒最多只能执行 60 次(仅在需要时)。
CSS 转换通过从一种状态转换到另一种状态来工作。为此,他们查看元素的最后计算值以创建初始状态。
由于浏览器仅在需要时才重新计算计算出的样式,因此在过渡开始时,您应用的所有 DOM 操作均无效。
所以在你的第一个场景中,当计算转换的初始状态时,我们有
.b { computedStyle: {display: none} }
......就是这样。
因为,是的,这display: none就是 CSSOM 的强大之处;如果一个元素有display: none,那么它不需要被绘制,它不存在。
所以我什至不确定转换算法是否会启动,但即使它启动,初始状态对于任何可转换值也是无效的,因为所有计算值都为空。
你的.a元素可见从一开始就没有这个问题,并且可以转变。
如果你能使其与延迟(诱导工作$.animate),这是因为DOM MANIP“这确实改变之间的display财产,这种延迟DOM MANIP的执行”,做触发转换,浏览器确实触发一个回流(例如,因为屏幕垂直同步在两者之间启动并且绘制操作被触发)。
现在,这不是问题的一部分,但既然我们确实更了解发生了什么,我们也可以更好地控制它。
事实上,一些 DOM 方法确实需要有最新的计算值。举例来说Element.getBoundingClientRect,或element.offsetHeight或getComputedStyle(element).height等等。所有这些需要整个页面已经更新,以便拳击正确做出的计算值(例如一个元素可以有一个保证金推或多或少等)。
这意味着我们不必知道浏览器何时会触发回流,我们可以强制它在我们想要的时候这样做。
但是请记住,页面上的所有元素都需要更新,这不是一个小操作,如果浏览器手头宽大,这是有充分理由的。
所以最好偶尔使用它,每帧最多使用一次。
幸运的是,Web API 使我们能够在此绘制操作发生之前挂钩一些 js 代码:requestAnimationFrame。
所以最好的办法是在这个 pre-paint 回调中只强制我们重排一次,并从这个回调中调用所有需要更新值的东西。
$('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%');
});
})
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;
}
<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>
但说实话,设置起来并不总是那么容易,所以如果你确定它会偶尔被解雇,你可能会很懒惰并同步进行:
$('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%');
})
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;
}
<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>