触发附加元素的CSS转换
正如这个问题所观察到的那样,在新添加的元素上直接的CSS转换被忽略了 – 转换的最终状态立即被渲染。
例如,给定这个CSS(前缀在这里省略):
.box { opacity: 0; transition: all 2s; background-color: red; height: 100px; width: 100px; } .box.in { opacity: 1; }
这个元素的不透明度将被立即设置为1:
// Does not animate var $a = $('<div>') .addClass('box a') .appendTo('#wrapper'); $a.addClass('in');
我已经看到几种触发转换的方法来获得预期的行为:
// Does animate var $b = $('<div>') .addClass('box b') .appendTo('#wrapper'); setTimeout(function() { $('.b').addClass('in'); },0); // Does animate var $c = $('<div>') .addClass('box c') .appendTo('#wrapper'); $c[0]. offsetWidth = $c[0].offsetWidth $c.addClass('in'); // Does animate var $d = $('<div>') .addClass('box d') .appendTo('#wrapper'); $d.focus().addClass('in');
同样的方法适用于香草JS DOM操作 – 这不是jQuery特定的行为。
编辑 – 我正在使用Chrome 35。
JSFiddle (包括香草JS例子)。
- 为什么忽略附加元素的直接CSSanimation?
- 这些方法如何以及为什么起作用?
- 还有其他的方法吗?
- 哪个(如果有的话)是首选解决scheme?
不添加元素animation的原因是由浏览器批量重排。
添加元素时,需要回stream。 这同样适用于添加类。 但是,当你在单一的JavaScript循环中 ,浏览器有机会优化出第一个。 在这种情况下,只有单个(同时是初始和最终)样式值,所以不会发生转换。
setTimeout
技巧是有效的,因为它延迟了另外一个JavaScript循环的类的增加,所以渲染引擎有两个值,需要计算,因为有时间点,当第一个呈现给用户时。
批处理规则还有一个例外。 浏览器需要计算立即值,如果你正在尝试访问它。 其中一个值是offsetWidth
。 当你访问它时,触发回stream。 另外一个是在实际显示过程中单独完成的。 再次,我们有两个不同的风格值,所以我们可以插入他们的时间。
当这种行为是可取的时候,这实际上是很less的情况之一。 在DOM修改之间访问引起回stream的属性的大部分时间会导致严重的减速。
首选的解决scheme可能因人而异,但对于我来说, offsetWidth
(或getComputedStyle()
)的访问是最好的。 有些情况下, setTimeout
被触发时没有重新计算样式。 这种情况很less见,主要是在加载的网站上,但是它发生了。 那么你不会得到你的animation。 通过访问任何计算的样式,你迫使浏览器实际计算它。
使用jQuery
试试这个( 这里是一个例子 ):
var $a = $('<div>') .addClass('box a') .appendTo('#wrapper'); $a.css('opacity'); // added $a.addClass('in');
使用香草javaScript试试这个:
var e = document.createElement('div'); e.className = 'box e'; document.getElementById('wrapper').appendChild(e); window.getComputedStyle(e).opacity; // added e.className += ' in';
简单的想法:
getComputedStyle()刷新所有挂起的样式更改并强制布局引擎计算元素的当前状态,因此.css()的工作方式类似。
关于jQuery
站点的css()
:
.css()方法是从第一个匹配元素中获取样式属性的一种便捷方式,尤其是浏览器访问大多数属性的不同方式(基于标准的浏览器中的getComputedStyle ()方法与currentStyle和runtimeStyle Internet Explorer中的属性)以及浏览器对某些属性使用的不同术语。
您可以使用getComputedStyle()/css()
而不是setTimeout
。 你也可以阅读这篇文章的一些细节信息和例子。
请使用下面的代码,使用“focus()”
jQuery的
var $a = $('<div>') .addClass('box a') .appendTo('#wrapper'); $a.focus(); // focus Added $a.addClass('in');
使用Javascript
var e = document.createElement('div'); e.className = 'box e'; document.getElementById('wrapper').appendChild(e).focus(); // focus Added e.className += ' in';
@ Frizi的解决scheme,但有时我发现getComputedStyle
没有工作,当我改变某个元素的某些属性。 如果这不起作用,你可以尝试getBoundingClientRect()
,如下所示,我发现它是防弹的:
假设我们有一个元素el
,我们要在其上转变opacity
,但是el
是display:none; opacity: 0
display:none; opacity: 0
:
el.style.display = 'block'; el.style.transition = 'opacity .5s linear'; // reflow el.getBoundingClientRect(); // it transitions! el.style.opacity = 1;
我尝试使用requestAnimationFrame()
来允许浏览器在下一个可用框架上绘制,而不是试图强制立即重绘或样式计算。
在Chrome + Firefox中,浏览器优化渲染太多,所以这仍然没有帮助(在Safari中工作)。
我决定用setTimeout()
手动强制延迟,然后使用requestAnimationFrame()
负责任地让浏览器画图。 如果在超时结束之前追加没有被绘制,animation可能会被忽略,但它似乎可靠地工作。
setTimeout(function () { requestAnimationFrame(function () { // trigger the animation }); }, 20);
我select了20ms,因为它在60fps(16.7ms)时大于1帧,有些浏览器不会在超过5ms的时间内注册超时。
手指越过,应该强制animation开始到下一帧,然后在浏览器准备再次绘制时负责任地启动它。