干净的方式以编程方式使用JS的CSS转换?

Fab*_*tté 20 javascript css transition css3 css-transitions

正如标题所暗示的那样,是否有正确的方法来设置一些初始CSS属性(或类)并告诉浏览器将这些转换为另一个值?

例如(小提琴):

var el = document.querySelector('div'),
    st = el.style;
st.opacity = 0;
st.transition = 'opacity 2s';
st.opacity = 1;
Run Code Online (Sandbox Code Playgroud)

这不会为Chrome 29/Firefox 23中元素的不透明度设置动画.这是因为(来源):

[...]你会发现,如果你同时应用两组属性,那么浏览器会尝试优化属性更改,忽略初始属性并阻止转换.在幕后,浏览器在绘制之前批量处理属性更改,虽然通常会加快渲染速度,但有时会产生不利影响.

解决方案是在应用两组属性之间强制重绘.这样做的一个简单方法就是访问DOM元素的offsetHeight属性[...]

事实上,黑客确实可以在当前的Chrome/Firefox版本中运行.更新了代码(小提琴 - Run打开小提琴后点击再次运行动画):

var el = document.querySelector('div'),
    st = el.style;
st.opacity = 0;
el.offsetHeight; //force a redraw
st.transition = 'opacity 2s';
st.opacity = 1;
Run Code Online (Sandbox Code Playgroud)

但是,这是相当hackish,并报告在某些Android设备上不起作用.

另一个答案建议使用,setTimeout以便浏览器有时间执行重绘,但它也失败了,因为我们不知道重绘需要多长时间.猜测一个相当大的毫秒数(30-100?)以确保重绘意味着牺牲性能,不必要地闲置,希望浏览器在那么短的时间内执行一些魔术.

通过测试,我发现了另一种解决方案,它在最新的Chrome上运行良好,使用requestAnimationFrame(小提琴):

var el = document.querySelector('div'),
    st = el.style;
st.opacity = 0;
requestAnimationFrame(function() {
    st.transition = 'opacity 2s';
    st.opacity = 1;
});
Run Code Online (Sandbox Code Playgroud)

我假设requestAnimationFrame在执行回调之前等到下一次重绘开始之前,因此浏览器不会批量处理属性更改.这里不完全确定,但在Chrome 29上运行良好.

更新:经过进一步测试后,该requestAnimationFrame方法在Firefox 23上运行效果不佳 - 大多数时候似乎都失败了.(小提琴)

是否有适当的或推荐的(跨浏览器)方式来实现这一目标?

Nic*_*lay 11

目前还没有一种干净的方式(不使用CSS动画 - 请参阅James Dinsdale附近的答案,了解使用CSS动画的示例).有一个规范错误14617,遗憾的是自2011年提交以来没有采取行动.

setTimeout在Firefox中无法可靠地工作(这是设计使然).

我不确定requestAnimationFrame- 原始问题的编辑说它也不能可靠地工作,但我没有调查.(更新:看起来requestAnimationFrame 至少被一个Firefox核心开发人员认为是可以进行更多更改的地方,不一定能看到之前更改的效果.)

强制回流(例如通过访问offsetHeight)是一种可能的解决方案,但是为了使转换工作,它应该足以强制重新设置(即getComputedStyle):https://timtaubert.de/blog/2012/09/css-transitions-for-dynamically- 创建-DOM元素/

window.getComputedStyle(elem).opacity;
Run Code Online (Sandbox Code Playgroud)

请注意,仅运行getComputedStyle(elem)是不够的,因为它是懒惰计算的.我相信你从getComputedStyle询问哪个属性并不重要,restyle仍然会发生.请注意,询问与几何相关的属性可能会导致更昂贵的回流.

关于reflow/restyle/repaint的更多信息:http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/


mik*_*1aj 6

情况自2013年以来发生了变化,所以这是一个新的答案:

您可以使用Web动画.它们在Chrome 36和Firefox 40 中原生实现,并且所有其他浏览器都有一个polyfill.

示例代码:

var player = snowFlake.animate([
  {transform: 'translate(' + snowLeft + 'px, -100%)'},
  {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);

// less than 1500ms later...changed my mind
player.cancel();
Run Code Online (Sandbox Code Playgroud)

  • 由于Firefox也支持40版(也是移动版)网络动画:https://developer.mozilla.org/en-US/docs/Web/API/Animation. (2认同)