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

CSS动画序列执行异常:首个动画生效,后续动画无响应

问题解决:连续触发CSS动画时第二个动画不执行

问题场景

用纯HTML元素、CSS和JavaScript开发游戏时,对象消亡流程需要先播放死亡动画,再执行后续动画,但第一个动画结束后,第二个动画未触发。复现代码如下:

JavaScript代码

let element = document.getElementById("text");
element.classList.add("popIn");
setTimeout(()=>{
    element.classList.remove("popIn");
    element.classList.add("popOut");
}, 500/*动画时长*/);

CSS代码

@keyframes anim{
  from{
    opacity: 0;
    transform: scale(0);
  }
  to{
    opacity: 1;
    transform: scale(1);
  }
}
.popIn, .popOut{
  animation-duration: 0.5s;
  animation-name: anim;
  animation-timing-function: cubic-bezier(0.6, 1.5, 0.64, 1);
}
.popOut{
  animation-direction: reverse;
}

HTML代码

<p id="text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eget dui egestas, pretium purus non, ultricies ligula. Fusce ultricies congue nisi ut congue. Praesent euismod, massa vitae consequat dictum, enim lacus finibus tortor, eget auctor felis nisi ut enim. Aliquam vitae sapien lorem. Phasellus ac dolor ac leo pellentesque ornare. Mauris in sem vestibulum, elementum augue sit amet, ultricies purus. Aenean id justo nunc. Mauris sit amet eros pretium, commodo ex vitae, pellentesque odio. Vivamus vel nunc sit amet tellus lobortis viverra. Donec consequat massa urna, in maximus justo placerat sit amet. Etiam non dui in est lobortis vulputate.
</p>

问题原因

浏览器会对同一事件循环内的DOM操作进行批量优化,当你在同一个setTimeout回调里同时移除popIn类并添加popOut类时,浏览器会合并这两个操作,不会实时计算样式变化,导致popOut的动画因状态未被感知而无法触发。

解决方案

方法1:用requestAnimationFrame延迟添加类

将添加popOut类的操作放到下一帧,让浏览器先处理完移除popIn后的样式渲染:

let element = document.getElementById("text");
element.classList.add("popIn");
setTimeout(()=>{
    element.classList.remove("popIn");
    requestAnimationFrame(() => {
        element.classList.add("popOut");
    });
}, 500/*动画时长*/);

方法2:强制触发样式重排

在移除类和添加类之间,读取一个会触发重排的DOM属性(如offsetHeight),强制浏览器计算当前样式状态:

let element = document.getElementById("text");
element.classList.add("popIn");
setTimeout(()=>{
    element.classList.remove("popIn");
    // 读取offsetHeight触发重排
    element.offsetHeight;
    element.classList.add("popOut");
}, 500/*动画时长*/);

额外优化:设置动画填充模式

为动画添加animation-fill-mode: forwards,确保动画结束后保持最终状态,避免状态残留影响后续动画:

.popIn, .popOut{
  animation-duration: 0.5s;
  animation-name: anim;
  animation-timing-function: cubic-bezier(0.6, 1.5, 0.64, 1);
  animation-fill-mode: forwards; /* 动画结束后保留最后一帧状态 */
}
.popOut{
  animation-direction: reverse;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 08:54:20