如果元素开始隐藏,css过渡将不起作用 [英] css transition doesn't work if element start hidden

查看:76
本文介绍了如果元素开始隐藏,css过渡将不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有2个div,其中一个通过display:none;隐藏.两者在属性right上具有相同的CSS过渡.

I have 2 divs, one of them is hidden via display:none;. Both have the same css transition on the property right.

如果我通过JQuery更改属性right并显示隐藏的div(通过使用$.css('display','none')$.show()$.toggle()等),则隐藏的div会立即在结束位置绘制

If I change the property right through JQuery and show the hidden div, either by using $.css('display','none') or $.show() or $.toggle() etc., the hidden div draw instantly at the ending position

$('button').on('click',function(){
  $('.b').show();
  $('.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>

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

If I use $.animate() it will work. But my question is ; Is that a bug or a normal behavior?

编辑不是显示的过渡:属性导致这里的问题与动画显示属性或可见性无关

Edit Not a duplicate of Transitions on the display: property cause the problem here is not about animating the display property nor the visibility

推荐答案

要清楚地了解这种情况,您需要了解CSSOM和DOM之间的关系.

To understand plainly the situation, you need to understand the relation between the CSSOM and the DOM.

先前的问题/文章中,我开发了有关 redraw 流程的工作原理.
基本上,它包含三个步骤,DOM操作,重排和绘制.

In a previous Q/A, I developed a bit on how the redraw process works.
Basically, there are three steps, DOM manipulation, reflow, and paint.

  • 第一个( DOM操作)只是修改js对象,并且都是同步的.
  • 第二个( reflow ,又名 layout )是我们感兴趣的第二个,它稍微复杂一点,因为只有某些DOM方法和绘画操作需要它.它包括更新所有CSS规则并重新计算页面上每个元素的所有计算样式.
    作为一项非常复杂的操作,浏览器将尝试尽可能少地执行此操作.
  • 第三次( paint )每秒最多只能完成60次(仅在需要时).
  • The first (DOM manipulation) is just modifying a js object, and is all synchronous.
  • The second (reflow, a.k.a layout) is the one we are interested in, and a bit more complex, since only some DOM methods and the paint operation need it. It consists in updating all the CSS rules and recalculating all the computed styles of every elements on the page.
    Being a quite complex operation, browsers will try to do it as rarely as possible.
  • The third (paint) is only done 60 times per seconds at max (only when needed).

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

CSS transitions work by transitioning from a state to an other one. And to do so, they look at the last computed value of your element to create the initial state.
Since browsers do recalculate the computed styles only when required, at the time your transition begins, none of the DOM manipulations you applied are effective yet.

所以在您的第一种情况下,当计算过渡的初始状态时,我们有

So in your first scenario, when the transition's initial state is calculated we have

.b { computedStyle: {display: none} }

...就是这样.

因为,是的,这就是CSSOM的display: none功能强大;如果元素具有display: none,则无需绘制,就不存在.

Because, yes, that's how powerful display: none is for the CSSOM; if an element has display: none, then it doesn't need to be painted, it doesn't exist.

因此,我什至不确定转换算法是否会启动,但是即使这样,初始状态对于 any 可转换值也将是无效的,因为所有计算值都为null.

So I'm not even sure the transition algorithm will kick in, but even if it did, the initial state would have been invalid for any transitionable value, since all computed values are just null.

您的.a元素自开始就可见,不存在此问题,可以进行过渡.

Your .a element being visible since the beginning doesn't have this issue and can be transitioned.

并且如果您能够使其延迟工作(由$.animate引起),那是因为在确实更改了display属性的DOM manip'和此触发的延迟DOM manip'的执行之间在过渡期间,浏览器确实触发了重排(例如,因为屏幕之间出现了垂直同步,并且触发了 paint 操作).

And if you are able to make it work with a delay (induced by $.animate), it's because between the DOM manip' that did change the display property and the execution of this delayed DOM manip' that does trigger the transition, the browser did trigger a reflow (e.g because the screen v-sync kicked in between and that the paint operation fired).

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

Now, it is not part of the question, but since we do understand better what happens, we can also control it better.

实际上,某些DOM方法确实需要具有最新的计算值.例如Element.getBoundingClientRectelement.offsetHeightgetComputedStyle(element).height等.所有这些都需要整个页面才能具有更新的计算值,以便正确进行装箱(例如,一个元素可以有一个空白或多或少地推动它,等等.

Indeed, some DOM methods do need to have up-to-date computed values. For instance Element.getBoundingClientRect, or element.offsetHeight or getComputedStyle(element).height etc. All these need the entire page to have updated computed values so that the boxing are made correctly (for instance an element could have a margin pushing it more or less, etc.).

这意味着我们不必知道浏览器何时触发此 reflow ,我们可以在需要时强制其执行.

This means that we don't have to be in the unknown of when the browser will trigger this reflow, we can force it to do it when we want.

但是请记住,页面上的所有元素都需要更新,这不是一个小操作,如果浏览器宽容地这样做,则有充分的理由.

But remember, all the elements on the page needs to be updated, this is not a small operation, and if browsers are lenient to do it, there is a good reason.

因此最好偶尔使用它,每帧最多使用一次.

So better use it sporadically, at most once per frame.

幸运的是,Web API使我们能够在绘画操作发生之前就挂接一些js代码:

Luckily, the Web APIs have given us the ability to hook some js code just before this paint operation occurs: requestAnimationFrame.

因此,最好的方法是在此预绘画回调中仅强制执行一次重排,并从该回调中调用需要更新值的所有内容.

So the best is to force our reflow only once in this pre-paint callback, and to call everything that needs the updated values from this callback.

$('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>

但是,老实说,设置起来并不总是那么容易,因此,如果您确定某些东西会偶尔被触发,那么您可能会很懒惰并同步执行所有操作:

But to be honest, it's not always easy to set up, so if you are sure it is something that will get fired sporadically, you may be lazy and do it all synchronously:

$('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>

这篇关于如果元素开始隐藏,css过渡将不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆