暂时禁用CSS过渡效果的最干净方法是什么?

2022-08-30 00:37:45

我有一个DOM元素,应用了以下部分/全部效果:

#elem {
  -webkit-transition: height 0.4s ease;
  -moz-transition: height 0.4s ease;
  -o-transition: height 0.4s ease;
  transition: height 0.4s ease;
}

我正在编写一个正在调整此元素大小的jQuery插件,我需要暂时禁用这些效果,以便我可以顺利地调整其大小。

暂时禁用这些效果(然后重新启用它们)的最优雅方法是什么,因为它们可能从父母那里应用,也可能根本不应用。


答案 1

简答题

使用此 CSS:

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  transition: none !important;
}

再加上这个JS(没有jQuery)...

someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions

或者这个JS与jQuery...

$someElement.addClass('notransition'); // Disable transitions
doWhateverCssChangesYouWant($someElement);
$someElement[0].offsetHeight; // Trigger a reflow, flushing the CSS changes
$someElement.removeClass('notransition'); // Re-enable transitions

...或使用您正在使用的任何其他库或框架的等效代码。

解释

这实际上是一个相当微妙的问题。

首先,您可能希望创建一个“notransition”类,您可以将该类应用于元素以将其CSS属性设置为。例如:*-transitionnone

.notransition {
  -webkit-transition: none !important;
  -moz-transition: none !important;
  -o-transition: none !important;
  transition: none !important;
}

(次要的一面 - 请注意那里缺少-ms-transition。你不需要它。第一个支持转换的Internet Explorer版本是IE 10,它不支持它们。

但这只是风格,而且很容易。当你来尝试使用这个类时,你会遇到一个陷阱。陷阱在于,像这样的代码不会像你天真地期望的那样工作:

// Don't do things this way! It doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
someElement.classList.remove('notransition')

天真地,您可能会认为高度的变化不会动画化,因为它发生在应用“notransition”类时。但实际上,至少在我尝试过的所有现代浏览器中,它都是动画的。问题在于,浏览器正在缓存它需要进行的样式更改,直到JavaScript完成执行,然后在单个重流中进行所有更改。因此,它会执行一个重排,其中对是否启用转换没有净变化,但高度有净变化。因此,它会对高度变化进行动画处理。

您可能认为解决此问题的合理而干净的方法是将“notransition”类的删除包装在1ms超时中,如下所示:

// Don't do things this way! It STILL doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
setTimeout(function () {someElement.classList.remove('notransition')}, 1);

但这也不能可靠地工作。我无法在WebKit浏览器中进行上述代码破解,但是在Firefox上(在慢速和快速机器上),您有时会(看似随机)获得与使用朴素方法相同的行为。我想这样做的原因是JavaScript执行可能足够慢,以至于超时函数在浏览器空闲时等待执行,否则会考虑进行机会性重排,如果发生这种情况,Firefox会在重排之前执行排队的函数。

我发现这个问题的唯一解决方案是在删除“notransition”类之前强制重排元素,刷新对它所做的CSS更改。有多种方法可以做到这一点 - 请参阅此处了解一些方法。最接近“标准”方法是读取元素的属性。offsetHeight

因此,一个实际有效的解决方案是

someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions

这里有一个JS小提琴,说明了我在这里描述的三种可能的方法(一种成功的方法和两种不成功的方法):http://jsfiddle.net/2uVAA/131/


答案 2

我主张按照DaneSoul的建议禁用动画,但要使切换全局:

/*kill the transitions on any descendant elements of .notransition*/
.notransition * { 
  -webkit-transition: none !important; 
  -moz-transition: none !important; 
  -o-transition: none !important; 
  -ms-transition: none !important; 
  transition: none !important; 
} 

.notransition然后可以应用于元素,有效地覆盖页面上的任何过渡动画:body

$('body').toggleClass('notransition');