触发附加元素的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 ,但是eldisplay: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开始到下一帧,然后在浏览器准备再次绘制时负责任地启动它。