不能靠 animation-play-state 恢复,必须用 class 切换 + 强制 reflow 才能真正重播;因后台冻结时时间轴持续走,resume 仅续播而非重播,且 ios safari 对该属性支持差,需移除类、offsetheight 触发 reflow、再添加类,并确保 animation-fill-mode: none。

直接说结论:不能靠 animation-play-state 恢复,必须用 class 切换 + 强制 reflow 才能真正重播。
为什么 animation-play-state = 'running' 在后台回来后没用
浏览器冻结动画时,只是挂起渲染帧,时间轴照常走——你设 animationPlayState = 'running' 只是告诉它“继续播放”,但它会从冻结时刻接着走,比如后台停了 12 秒,回来就直接跳到第 12 秒的帧。
更糟的是:getComputedStyle(el).animationPlayState 还可能返回 "running",但实际一帧都没渲染;iOS Safari(尤其微信 WebView)对这个属性的支持极差,经常忽略或延迟生效。
可靠重播必须用 class 切换 + offsetHeight 强制 reflow
让浏览器“认为这是新动画”,而不是试图唤醒旧动画。关键三步缺一不可:
- 先移除动画类:
el.classList.remove('animate-bounce') - 强制同步 reflow:
el.offsetHeight(或getComputedStyle(el).opacity)——这一步不能省,否则 class 切换无效 - 再添加动画类:
el.classList.add('animate-bounce')
动画类里只写 animation 声明,且必须设 animation-fill-mode: none,避免 forwards 把终态样式锁死,导致重播失败。
移动端和 iOS Safari 的坑要单独填
visibilitychange 在 iOS 上响应慢(延迟 200–500ms),甚至息屏时不触发;微信 WebView 还常把 document.visibilityState 错报为 'visible'。所以:
- 别只监听
visibilitychange,同时加pagehide、pageshow、focus事件兜底 - 重播逻辑包一层
setTimeout(() => { /* 上述三步 */ }, 100) - 如果元素有 JS 设置过的
el.style.transform,重播前先清空:el.style.transform = '',否则和 CSS 动画里的 transform 冲突,导致跳帧或不触发 -
animation-duration别小于0.15s(150ms),部分 Android Webview 会直接跳过首帧
别碰 animation-fill-mode: forwards 或 backface-visibility
这两个属性和后台冻结完全无关,强行加只会引入新问题:forwards 让动画停在终态后,再设 running 根本不会重播;backface-visibility 对合成层控制没用,还可能干扰硬件加速。
真正要管的,是动画生命周期本身——它什么时候该启、什么时候该重置、有没有被内联样式劫持。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











