通过 JavaScript 分配 CSS 转换时不起作用

Posted

技术标签:

【中文标题】通过 JavaScript 分配 CSS 转换时不起作用【英文标题】:CSS transitions do not work when assigned trough JavaScript 【发布时间】:2012-01-02 21:04:24 【问题描述】:

尝试通过 javascript 将 CSS3 转换应用于幻灯片时,我头疼不已。

基本上,JavaScript 会获取幻灯片中的所有幻灯片并将 CSS 类应用于正确的元素以提供漂亮的动画效果,如果不支持 CSS3 过渡,它只会应用没有过渡的样式。

现在,我的“小”问题。所有工作都按预期工作,所有幻灯片都有正确的样式,代码运行没有错误(到目前为止)。但是,即使应用了正确的样式,指定的过渡也不起作用。此外,当我自己通过检查器应用样式和过渡时,它们会起作用。

由于我自己找不到合乎逻辑的解释,所以我认为这里有人可以回答,好吗?

我已经整理了一个小例子来说明现在的代码:http://g2f.nl/38rvma 或者使用 JSfiddle(无图片):http://jsfiddle.net/5RgGV/1/

【问题讨论】:

【参考方案1】:

要使transition 工作,必须完成三件事。

    元素必须明确定义属性,在这种情况下:opacity: 0; 元素必须定义转换:transition: opacity 2s; 必须设置新属性:opacity: 1

如果您像在示例中那样动态分配 1 和 2,则需要在 3 之前有一个延迟,以便浏览器可以处理请求。当您调试它时它起作用的原因是您通过单步执行它来创建此延迟,从而使浏览器有时间进行处理。延迟分配.target-fadein

window.setTimeout(function() 
  slides[targetIndex].className += " target-fadein";
, 100); 

或将.target-fadein-begin 直接放入您的 html 中,以便在加载时对其进行解析并为转换做好准备。

transition 添加到元素不会触发动画,更改属性会。

// Works
document.getElementById('fade1').className += ' fade-in'

// Doesn't work
document.getElementById('fade2').className = 'fadeable'
document.getElementById('fade2').className += ' fade-in'

// Works
document.getElementById('fade3').className = 'fadeable'

window.setTimeout(function() 
  document.getElementById('fade3').className += ' fade-in'
, 50)
.fadeable 
  opacity: 0;


.fade-in 
  opacity: 1;
  transition: opacity 2s;
<div id="fade1" class="fadeable">fade 1 - works</div>
<div id="fade2">fade 2 - doesn't work</div>
<div id="fade3">fade 3 - works</div>

【讨论】:

非常感谢您的帮助!我回家后会调查此事。 是的,似乎是唯一要做的事情。太糟糕了,没有关于何时应用样式的事件。我想我只需要等待 100 毫秒 :) 我刚刚遇到了这个问题,我花了几个小时才发现在应用过渡属性后需要一段时间。有什么知道为什么会这样,因为通常样式会立即应用?这是错误还是预期行为? 在我的情况下,我在 setTimeout 中使用了 0 毫秒的延迟,它仍然有效。 有没有办法用promise 来做,或者我们不能因为 className 分配不是异步对象而做?【参考方案2】:

有些人问为什么会有延迟。该标准希望允许同时发生多个过渡,称为样式更改事件(例如元素在旋转到视图中的同时淡入)。不幸的是,它没有定义一种明确的方式来对您希望同时发生的转换进行分组。相反,它允许浏览器通过调用它们的距离来任意选择同时发生的转换。大多数浏览器似乎使用它们的刷新率来定义这个时间。

如果您想了解更多详情,请参考以下标准: http://dev.w3.org/csswg/css-transitions/#starting

【讨论】:

【参考方案3】:

欺骗布局引擎!

function finalizeAndCleanUp (event) 
    if (event.propertyName == 'opacity') 
        this.style.opacity = '0'
        this.removeEventListener('transitionend', finalizeAndCleanUp)
    

element.style.transition = 'opacity 1s'
element.style.opacity = '0'
element.addEventListener('transitionend', finalizeAndCleanUp)
// next line's important but there's no need to store the value
element.offsetHeight
element.style.opacity = '1'

如前所述,transitions 通过从状态 A 到状态 B 进行插值来工作。如果您的脚本在同一个函数中进行了更改,布局引擎无法区分状态 A 结束的位置和 B 开始的位置。除非你给它一个提示。

由于没有官方的方式来制作提示,你必须依赖某些函数的副作用。在这种情况下,.offsetHeight getter 隐式使布局引擎停止、评估和计算所有设置的属性,并返回一个值。通常,出于性能影响,应该避免这种情况,但在我们的例子中,这正是我们所需要的:状态整合。

为完整性添加了清理代码。

【讨论】:

为了强调重要的行是一个未赋值的计算表达式,你可以写void element.offsetHeight 关于transitionend 的注意事项:不保证会触发此事件。例如,如果选项卡不可见,浏览器可能会将其优化掉。不要依赖它或有后备! 感谢您的提醒,@transistor09

以上是关于通过 JavaScript 分配 CSS 转换时不起作用的主要内容,如果未能解决你的问题,请参考以下文章

使用 JavaScript 触发 CSS 转换 [重复]

如何从 JavaScript 设置和触发 css 转换

为啥使用 javascript / jQuery 添加类时 css 转换不起作用?

切换时如何在javascript和css中转换为“隐藏”

使用 javascript 为 CSS3 转换的值设置动画是不是会排除硬件加速?

rails 4.0.0 处理 css.scss 路径,但在预编译时不附加哈希