浏览器标签页切换时css动画挂起是正常节电行为;animation-play-state设为running无效,因时间轴被冻结;唯一可靠重播方式是class移除+offsetheight强制reflow+class添加,且需配合animation-fill-mode:none等约束。

浏览器切换到其他标签页时,CSS 动画被挂起是正常节电行为,不是 bug,所有主流浏览器(Chrome、Firefox、Safari、Edge)都会这么做。
为什么 animation-play-state = 'running' 无效
动画时间轴被浏览器冻结后,animation-play-state 属性值仍读作 "running",但合成线程根本不推进帧。此时 JS 设置 el.style.animationPlayState = "running" 不会重置时间轴,只会让动画从“后台停留的秒数”处继续——比如停了 8.3 秒,回来就直接跳到第 8.3 秒的帧,用户看不到重播效果。iOS Safari 对该属性 runtime 修改支持极差,常静默忽略。
class 切换 + offsetHeight 是唯一可靠重播方式
必须让浏览器认为“这是一个新动画实例”,而非唤醒旧动画。关键三步缺一不可:
- 先移除动画类:
el.classList.remove('animate-bounce') - 立即触发同步 reflow:
el.offsetHeight(不能用offsetWidth或getBoundingClientRect()替代,只有offsetHeight能保证强制 layout) - 再添加动画类:
el.classList.add('animate-bounce')
配套 CSS 必须设 animation-fill-mode: none;若用了 forwards,元素已锁死终态样式,再怎么 class 切换也不会触发新周期。
iOS Safari 和微信 WebView 的硬性兼容要求
这些环境对 visibilitychange 响应延迟明显(200–500ms),甚至息屏时不触发,还可能错报 document.visibilityState 为 "visible"。所以:
- 别只监听
visibilitychange,必须同时加pageshow、focus事件兜底 - 重播逻辑包一层
setTimeout(() => { /* 上述三步 */ }, 100) - 重播前清空内联 transform:
el.style.transform = '',否则和 CSS @keyframes 中的transform冲突导致跳帧或不触发 -
animation-duration不能低于0.15s(150ms),否则部分 Android WebView 会跳过首帧
真正卡住的地方往往不是写法本身,而是没意识到:浏览器不会替你撤销终态样式,也不会在 DOM 重建后自动恢复动画上下文——每次切回,都得当成一个全新起点来处理重排时机、内联样式和事件响应延迟。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











