animationend 比 settimeout 更可靠,因其基于浏览器真实动画完成信号,不受暂停、动态时长或系统偏好影响;需过滤 animationname、处理 infinite 动画、兼容旧 safari,并在回调中先清动画、强制重排再改样式。

直接监听 animationend 事件并操作 class 或内联样式,是最可靠的方式;用 setTimeout 模拟时长容易因动画被中断、加速或系统性能波动而失准。
为什么 animationend 比 setTimeout 更值得依赖
浏览器原生的 animationend 是动画真实完成的信号,它不依赖你写的时长是否匹配——比如用户启用了“减少运动”偏好、CSS 中用了 animation-duration: calc(...)、或动画被 animation-play-state: paused 中断后恢复,animationend 都只在实际播完最后一帧时触发。而 setTimeout 只是“猜时间”,一旦动画没播完(比如被暂停、跳过、或 JS 动态改了 duration),后续样式变更就会错位或提前发生。
- 必须用
event.animationName过滤目标动画,避免多个动画共存时误响应 - 若动画设置了
animation-iteration-count: infinite,animationend不会触发——此时需改用animationiteration - 旧版 Safari 需同时监听
webkitAnimationEnd,但现代项目可忽略(除非仍需支持 iOS 9 以下)
监听 animationend 后怎么安全地改样式
常见错误是在回调里直接写 element.style.opacity = '0' 或 element.classList.add('hidden'),结果样式没生效,或下次动画无法再触发。根本原因是:动画结束帧(由 animation-fill-mode: forwards 保持)和你新设的样式可能冲突,或者浏览器尚未完成样式清理。
- 先移除动画本身:
element.style.animationName = 'none',切断动画对样式的持续控制 - 强制重排(reflow):
element.offsetHeight—— 这一步让浏览器同步应用上一步的清除动作 - 再执行你的样式变更,比如
element.classList.add('done')或element.style.transform = 'translateY(0)' - 如果后续还要重播动画,记得在变更后重新赋值
animationName,不能只靠 class 切换
transitionend 和 animationend 别混用
如果你用的是 transition(比如 hover 时 transform 变化),就该监听 transitionend;如果是 @keyframes + animation,就必须用 animationend。两者触发机制完全不同:transitionend 在单次属性变化完成时触发,且可能因多属性过渡(如同时改 opacity 和 transform)触发多次;animationend 对应整个动画序列的终结,哪怕它包含几十个关键帧。
-
transitionend的event.propertyName可用来区分是哪个 CSS 属性变完了 -
animationend的event.elapsedTime能告诉你这次播了多久(可用于调试动态 duration) - 都建议加
{ once: true }选项,防止重复绑定导致逻辑执行多次
真正难的不是监听事件,而是处理动画结束后的样式残留和状态重置——尤其是当元素同时受 transition、transform、inline style 和多个 class 影响时,顺序和时机稍有偏差,视觉就会卡住或跳变。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











