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

时间:2019-01-24 10:56:01

标签: javascript jquery css

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

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

$('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(),它将起作用。但是我的问题是;那是错误还是正常行为?

编辑不是Transitions on the display: property的重复项,这是因为这里的问题不是关于动画显示属性还是可见性

6 个答案:

答案 0 :(得分:2)

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

previous Q/A中,我对 redraw 过程的工作方式进行了一些开发。
基本上,分三个步骤,DOM操作,重排和绘制。

  • 第一个( DOM操作)只是修改js对象,并且都是同步的。
  • 第二个( reflow ,又称​​ layout )是我们感兴趣的第二个,它稍微复杂一点,因为只有某些DOM方法和绘画操作需要它。它包括更新所有CSS规则并重新计算页面上每个元素的所有计算样式。
    作为一项相当复杂的操作,浏览器将尝试尽可能少地执行此操作。
  • 第三次( paint )每秒最多只能完成60次(仅在需要时)。

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

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

.b { computedStyle: {display: none} }

...就是这样。

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

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

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

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


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

实际上,some DOM methods确实需要具有最新的计算值。例如Element.getBoundingClientRectelement.offsetHeightgetComputedStyle(element).height等。所有这些都需要整页具有更新的计算值,以便正确进行装箱(例如元素可以有一定的余量或多或少地推动它,等等。

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

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

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

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

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

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

答案 1 :(得分:1)

没有参数的jQuery show()基本上做到了

$('.b').css('display', 'block');

所以你在做什么

$('button').on('click',function(){
  $('.b').css('display', 'block');
  $('.b').css('right','80%');
  $('.a').css('right','80%');
})

基本上与

相同
$('button').on('click',function(){
  $('.b').css({
    'display': 'block',
    'right': '80%'
  });
  $('.a').css('right','80%');
})

但是,如果同时更改显示,则您将无法转换任何内容。

将持续时间值添加到show()调用中,所做的事情比仅更改display更为复杂。它将像这样将元素添加到动画队列中

$('.b').css({
  'display': 'block', 
  'overflow': 'hidden',
  'height': '0',
  'width': '0',
  'margin': '0',
  'width': '0',
  'opacity': '0'
});
$('.b').animate({
  height: '50px',
  padding: '0px',
  margin: '0px', 
  width: '50px', 
  opacity: '1'
}, 0)

因此,您需要做的是将0的持续时间值(零)作为参数放入show()调用中

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

答案 2 :(得分:0)

我个人将其显示后检查该元素是否可见。然后继续通过jQuery。

像这样:gulp-eslint package description

$('button').on('click',function(){

    var b = $('.b');
    b.show();

    if(b.is(":visible")) {
        $('.b').css('right','80%');
        $('.a').css('right','80%');
    }

});

答案 3 :(得分:0)

在节目上开始过渡的问题与尝试在加载时开始过渡的问题相同。 CSS不支持在元素显示的同一帧上开始动画,这真是令人讨厌。在此处查看答案:css3 transition animation on load?

解决方案是使用关键帧而不是过渡。但是然后您就不能正确执行变量,因为CSS文件中的变量已被硬编码为80%。

$('button').on('click',function(){
  $('.b').show();
  $('.b').addClass('goLeft');
  $('.a').addClass('goLeft');
})
body {
  width:800px;
  height:800px;
}

div {
  width:50px;
  height:50px;
  background-color:#333;
  position:absolute;
  display:none;
  right:5%;
  top:0;
  color: white;
}

.goLeft {
  animation-name: goLeft;
  animation-duration: .5s;
  animation-iteration-count: 1;
  animation-play-state: running;
  animation-fill-mode: forwards;
  animation-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1);
}

.a {
  display:block;
  top:60px;
}

@keyframes goLeft {
  from {
    right: 5%;
  }
  to {
    right: 80%;
  }
}
<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>

答案 4 :(得分:-1)

检查这个小提琴只需添加一个超时时间

$('button').on('click',function(){
  $('.b').show();
  setTimeout(function(){ $('.b').css('right','80%');
  $('.a').css('right','80%'); }, 0);
  
})
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);
}

.a {
  display:block;
  top:20%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='a'>
</div>
<div class='b'>
</div>
<button>Launch</button>

https://jsfiddle.net/qu2pybch/

答案 5 :(得分:-2)

这不是错误,jQuery show()函数使用400ms来显示元素,只需使用show(0)

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