如果元素开始隐藏,css 过渡不起作用
Posted
技术标签:
【中文标题】如果元素开始隐藏,css 过渡不起作用【英文标题】:css transition doesn't work if element start hidden 【发布时间】:2019-06-18 02:11:27 【问题描述】:我有 2 个div
s,其中一个通过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的重复项,因为这里的问题不在于动画显示属性也不在于可见性
【问题讨论】:
正常行为,在显示和更改右侧之间需要稍许延迟 我正要说“好的”,但你这里的正常行为是什么?不起作用的东西不能是正常的行为,对吗?这只是一个错误,一个众所周知的错误,我可以理解,我会处理它,但仍然是一个错误,不是吗? 这不是重复的。我怎么能否认呢? 这不是一个错误,但它应该是这样工作的......是的,它不是重复的。我没有必要的语言来解释它,但其他人会来更好地解释 检查这个:jsfiddle.net/r7016skg 添加一个小的延迟将输出你所期望的 【参考方案1】:要想清楚地理解这种情况,你需要了解 CSSOM 和 DOM 之间的关系。
在previous Q/A 中,我对重绘 过程的工作原理做了一些说明。 基本上,有三个步骤,DOM 操作、回流和绘制。
第一个(DOM操作)只是修改一个js对象,都是同步的。 第二个(reflow,又名 layout)是我们感兴趣的,而且有点复杂,因为只有一些 DOM 方法和绘制操作需要它.它包括更新所有 CSS 规则并重新计算页面上每个元素的所有计算样式。 作为一个相当复杂的操作,浏览器会尽可能少地尝试这样做。 第三次 (paint) 每秒最多只能完成 60 次(仅在需要时)。CSS 转换通过从一种状态转换到另一种状态来工作。为此,他们会查看元素的最后计算值以创建初始状态。 由于浏览器仅在需要时才重新计算计算样式,因此在您的过渡开始时,您应用的任何 DOM 操作都还没有生效。
所以在您的第一个场景中,当计算转换的初始状态时,我们有
.b computedStyle: display: none
...就是这样。
因为,是的,这就是 display: none
对于 CSSOM 的强大之处;如果一个元素有display: none
,那么它不需要被绘制,它不存在。
所以我什至不确定转换算法是否会启动,但即使它启动了,初始状态对于 任何 可转换值都是无效的,因为所有计算值都只是空值。
您的.a
元素从一开始就可见,不存在此问题,可以进行转换。
如果您能够使其延迟工作(由 $.animate
引起),那是因为在确实更改了 display
属性的 DOM manip' 和确实触发的延迟的 DOM manip' 执行之间过渡时,浏览器确实触发了 reflow(例如,因为屏幕垂直同步在两者之间启动,并且触发了 paint 操作)。
现在,这不是问题的一部分,但由于我们确实更好地了解发生了什么,我们也可以更好地控制它。
确实,some DOM methods 确实需要有最新的计算值。例如Element.getBoundingClientRect
、element.offsetHeight
或getComputedStyle(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>
【讨论】:
好的,我将其标记为最佳。这很有指导意义,谢谢!如果我正确理解您解释的内容和$.show()
的jquery 文档,@yunzen 是正确的,因为调用$.show()
作为动画将在最后触发重排,对吗? ($.show(0)
)
@vincent-d 当持续时间为0
时,我无法清楚地了解 jQuery 源代码中发生的情况,但是是的,肯定有一些东西会触发回流。如果你有比我更多的时间来完成演练,入口is here。
@Kaiido 再次感谢你,这很有启发性和趣味性。我正在阅读 jquery 来解决这个问题,如果我找到了,我会告诉你。无论如何,当你使用$.show()
传递参数时,肯定会有回流,因为这个评论Set the display of the elements in a second loop to avoid constant reflow
所以我正在看的是全局变量,我发现ownerDocument()
和appendChild()
但是我在您在此处提供的列表中找不到它们gist.github.com/paulirish/5d52fb081b3570c81e3a
哦,appenChild() 立即带有removeChild()
。每次调用 show()
时都会发生这种情况,但如果您没有输入参数,则不会发生这种情况,因为它只会与 display 属性打交道(显然可以避免回流)
@Kaiido 也许我的javascript知识有点差,所以告诉我这是否可能是回流的原因:D【参考方案2】:
不带参数的jQueryshow()
基本上就是这样做的
$('.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>
【讨论】:
来自文档:The matched elements will be revealed immediately, with no animation. This is roughly equivalent to calling .css( "display", "block" ),
api.jquery.com/show
通过添加一个数字,您可以使其动画化并解决问题(任何数字都可以,甚至高于 0)
@TemaniAfif 这就是文档所说的。它还说默认持续时间是 400
。我很困惑
有不同的方法调用......有和没有参数。没有动画的 on 没有动画,而有参数的动画……动画的那个正在解决问题
这很奇怪,但结果是完美的。在我回到这里之前(我以为我的帖子永远丢失了哈哈)我想出了 show() 回调。是的,正如@TemaniAfif 解释的那样,它确实应用了 400 毫秒。所以我验证了这个解决方案,因为它是最好的恕我直言【参考方案3】:
我个人会检查元素在显示后是否可见。然后继续通过 jQuery。
像这样:https://jsfiddle.net/789su6xb/
$('button').on('click',function()
var b = $('.b');
b.show();
if(b.is(":visible"))
$('.b').css('right','80%');
$('.a').css('right','80%');
);
【讨论】:
请解释您的反对意见。这是一个可以接受的答案。 我没有投反对票,但我明白为什么。我说我不想使用 $.animate 并让 css 转换完成他的工作,所以如果我不想这样做,我也不想要一个必须检查 dom 状态的解决方案 你的问题很不清楚。每个人都因为为您当前使用的代码提供改进的代码而遭到反对。你问这是否是正常行为,是的。您的代码只需要像我们的答案之一那样编写。您需要声明该节目是即时的。提供延迟。或者检查你的元素是否可见。 我的问题怎么不清楚?另外,检查验证答案,这是一个很好的答案,因为它是问题的解决方案,而不是获得类似“足够好”结果的偷偷摸摸的方法。 我不知道为什么你会被否决,你不应该得到它,但你的答案还不足以成为好的答案,我告诉你为什么。其他人被否决了,因为他们的回答完全 100% 偏离主题【参考方案4】:在节目中开始过渡的问题与尝试在加载时开始过渡的问题相同。 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>
【讨论】:
【参考方案5】:检查这个小提琴只需要添加一个超时
$('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/
【讨论】:
你不能把那里的codepen内容粘贴到这里的sn-p吗? @yunzen 感谢您的建议,下次会记住这一点。 你可以在这里制作一个完整的工作 sn-p。我为你做到了 谢谢。看起来很整洁以上是关于如果元素开始隐藏,css 过渡不起作用的主要内容,如果未能解决你的问题,请参考以下文章