You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

transitionEnd回调中用requestAnimationFrame修改display加CSS过渡失效问题

核心问题背景

将元素display属性从none改为block/flex等取值后,CSS过渡无法触发是前端常见问题,业界通用可行方案是使用requestAnimationFrame等待重排完成后再修改过渡属性,从而触发预期的动画效果。

方案失效的特殊场景

该通用方案在transitionend事件回调中执行时会失效,单次requestAnimationFrame无法触发预期过渡:

  • 基础复现场景:设置红蓝两个默认display: none的盒子,使用通用方案淡入蓝色盒子可正常生效
  • 异常表现:监听蓝色盒子的transitionend事件,触发后隐藏蓝色盒子,用相同逻辑显示红色盒子,红色盒子会直接显示,无opacity过渡效果
  • 对照案例:给红色盒子绑定onClick事件,用相同逻辑重新触发过渡可正常生效
复现代码

JS 代码

let elem = document.getElementById("elem");
let elem2 = document.getElementById("elem2");

function addFadeElem2() {
  elem2.classList.add("fade");
}

function switchElements() {
    elem.classList.remove("fade") ;
    elem2.style.display = "block";
    window.requestAnimationFrame(addFadeElem2);
}

elem.style.display = "block";
window.requestAnimationFrame(() => {
  elem.classList.add("fade");
});

function onTransitionEnd() {
  switchElements();
  //elem.offsetHeight; // 开启该行强制重排即可正常运行
  elem.removeEventListener("transitionend", onTransitionEnd);
}

elem.addEventListener("transitionend", onTransitionEnd);

function refade() {
  elem2.style.display = "none";
  elem2.classList.remove("fade");
  window.requestAnimationFrame(() =>
  {
    elem2.style.display = "block";
    window.requestAnimationFrame(addFadeElem2);
  });
}

CSS 代码

#elem {
  background-color: blue;
}

#elem2 {
  background-color: red;
}

.box {
  display: none;
  opacity : 0;
  transition: opacity 1s;
  transition-timing-function: ease-in;
  width: 300px;
  height: 250px;
}

.box.fade {
  transition-timing-function: ease-out;
  opacity: 1;
}

HTML 代码

<div id="elem" class="box">aaaaa</div>
<div id="elem2" class="box" onclick="refade()">bbbbbbb</div>
已验证的可行解决方案
  • 强制重排:设置display: block后通过访问offsetHeight属性主动触发重排,无需requestAnimationFrame即可正常触发过渡
  • 双requestAnimationFrame:嵌套两层requestAnimationFrame回调执行属性修改,也可解决过渡失效问题
观察到的底层现象

通过开发者工具性能面板分析可得:过渡失效时,requestAnimationFrame回调在display: block对应的重排执行前就已经触发,导致过渡属性修改和display属性修改被合并到同一帧渲染,无前后状态差因此无法触发过渡。

待解答疑问
  • 为什么transitionend事件回调场景下会出现单次requestAnimationFrame失效的情况?
  • 为什么页面加载、点击事件场景下通用方案可以正常生效?
  • 点击事件场景下方案生效,是否和display: block是在requestAnimationFrame回调中设置有关?

内容的提问来源于stack exchange,提问作者Johnny Fang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 02:15:04