我有一个jQuery插件,它可以在屏幕上移动元素,但有一个"animate"切换,可以显示幻灯片过渡或不显示。在尝试使用CSS转换而不是Javascript转换进行更改时,我遇到了这个问题,我不确定这是一个bug/怪癖,还是我做错了:
var $item = $('#myItem');
if (!animate) {
$item.removeClass('csstransitions'); // Class that has "transition:left 0.25s ease-out"
$('#myItem').css('left', '300px'); // Move the element
$('#myItem').addClass('csstransitions'); // Re-apply transitions class
}
当以这种方式进行时,在转换类没有应用于元素,而是在之后立即应用的情况下,css发生了更改,一些浏览器(在我的测试中,Chrome和Safari)仍然应用css转换,而根据我的逻辑,它应该只是捕捉到新位置。
在这个jsFiddle中看到这一点;在Chrome或Safari中,单击"无延迟"按钮,可以看到它仍然会对框的位置设置动画,而"延迟"按钮(使用一毫秒后设置的超时)不会对CSS更改设置动画。
正如jsFiddle中所指出的,我必须使用setTimeout调用(setTimeout(function() { $el.addClass('csstransition'); }, 1);
)才能在Chrome和Safari中获得正确的行为。这仅仅是因为CSS转换正在流血,还是我做错了什么,有一种更简单的方法可以暂时"切换"转换?
EDIT:注意到这个问题很相似,虽然这个问题的答案是只分离两个调用,但问题仍然是"为什么我们(web开发人员)需要分离这两个调用?"这是我们应该用来切换CSS转换的方法吗?
我会投票支持怪癖或实现差异。
在转换之前,应用哪种顺序样式并不重要,因为在实用性中,顺序并不重要,只重要特定性。但在转换中,样式中添加了时间延迟元素,这是问题的关键。
由于不知道任何浏览器是如何应用样式的,我可以猜测Safari和Chrome会进行一些优化,以便在每次样式更新后不必重新流动页面。相反,他们可能会等待特定的时间间隔或事件来进行更新,例如在代码块的末尾。
此处详细介绍了一些差异:
http://taligarsiel.com/Projects/howbrowserswork1.htm
不过,我不知道这个具体问题是否包括在内。
作为演示,处理此问题的另一种方法是使用2个点击处理程序:
$('button#nodelay').on('click', function() {
var $el = $('#square');
$el.removeClass('csstransition');
$el.css('left', '100px');
}).on('click', function() {
$el.addClass('csstransition');
});
这基本上将两个更新划分为单独的代码块,非常类似于setTimeout
方法。
此外,由于过渡仍然是草案标准,我不会依赖任何这种行为来保持一致,而且也有一些怪癖。(我遇到了一个问题,即不能在所有浏览器中同时转换left
和top
)。
编辑
为了进一步解释,如果浏览器在添加到DOM后立即渲染所有CSS,则会得到以下流:
- CSS
left: 300px
添加到DOM元素 - 渲染样式:浏览器检查图元上是否有过渡。如果是,请设置动画;如果不是,请立即应用。在这种情况下,还没有过渡,因此不会发生动画
- CSS
transition: left 2s ease-out
添加到DOM元素 - 渲染样式:无渲染更改,转换适用于未来的
left
更改
然而,如果浏览器通过在代码块(或类似的东西)的末尾分组来优化CSS的呈现,则会得到以下结果:
- CSS
left: 300px
添加到DOM元素 - CSS
transition: left 2s ease-out
添加到DOM元素 - 达到渲染点(代码块结束等),渲染所有样式:
- 应用
left
时,浏览器会检查图元上是否存在过渡。如果是,请设置动画;如果不是,请立即应用。在这种情况下,存在过渡,因此会发生动画
因此,动画的长度和等待的时间是无关紧要的。重要的是left: 300px
在应用转换之前被渲染。目前,在WebKit中,这意味着在一个单独的、比left
样式晚的代码块中应用transition
样式。这是通过建议的所有答案setTimeout
(具有0延迟的事件)、单独的单击处理程序(如果第二次应用)或函数回调来实现的。
这是另一种工作方式:
$('button#nodelay').on('click', function() {
var $el = $('#square');
$el.removeClass('csstransition');
$el.css('left', '100px').css('left'); // <-- This (or .show() even)
$el.addClass('csstransition');
});
这是有效的,因为您正在强制浏览器停止并评估CSS,以便获得left
的值(尽管您可以在第二个.css()
调用中放入任何有效的CSS属性)。在这种情况下,它应用所有CSS并强制元素重新渲染。