safari后台挂起css动画是webkit主动冻结时间轴的节电行为,需用visibilitychange+offsetheight强制重播:先移类、再触发reflow(el.offsetheight)、最后加类,且动画类必须设animation-fill-mode: none、时长≥150ms、清空内联transform。

为什么Safari后台会强制挂起CSS动画
不是代码问题,是WebKit主动冻结动画时间轴——iOS和macOS Safari在标签页不可见或窗口最小化时,会暂停所有animation的播放计时器,省电优先。切回来时,动画不会重播,而是从“挂起时刻”继续,造成卡顿、跳帧或直接停在中间态。
用visibilitychange + offsetHeight强制重播动画
核心是三步原子操作:移类 → 强制reflow → 加类。只靠classList.remove再add无效,必须插入同步布局触发点。
el.classList.remove('pulse')-
void el.offsetHeight(关键!不能省,否则浏览器跳过重排) el.classList.add('pulse')
动画类里必须设animation-fill-mode: none,禁用forwards——否则浏览器认为“已完成”,拒绝重播。
兜底监听pageshow和focus事件
iOS Safari对visibilitychange响应延迟严重,息屏时甚至不触发;微信WebView还会把document.visibilityState错报为'visible'。单靠一个事件不可靠。
- 同时绑定
visibilitychange、pageshow、focus三个事件 - 在
visibilitychange回调里加setTimeout(() => { /* 重播逻辑 */ }, 100),绕过初始延迟窗口 - 不要监听
pagehide——后台冻结不可逆,暂停动作无意义
动画属性与时长的硬性避坑项
很多重播失败不是逻辑错,而是底层限制被忽略:
-
animation-duration必须≥150ms,低于此值,部分iOS WebKit直接跳过首帧 - 重播前必须清空
el.style.transform等内联样式,否则和CSS动画里的transform冲突,导致跳变或不触发 - 禁用
backface-visibility——它和后台冻结无关,强行加反而干扰合成层,引发闪烁
最易漏的是内联transform残留,哪怕只执行过一次el.style.transform = 'scale(0.95)',也会让后续pulse动画从错误起点开始。每次重播前清空它,是硬性动作。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











