iPhone / iPad 上网页动画不生效的排查与修复
一个在 Chrome 上跑得很顺的页面,拿到 iPhone 或 iPad 上打开,动画全部静止、部分视觉效果消失。这类问题不是玄学,原因基本集中在几处,按顺序排查一般都能定位。
第一步:确认是「没触发」还是「没渲染」
打开桌面的 Safari,用「开发 → 你的设备」连接 iPhone/iPad,检查元素的 animation-name 和 animation-play-state:
const el = document.querySelector(".quote-char");
getComputedStyle(el).animationName; // 期望不是 "none"
getComputedStyle(el).animationPlayState; // 期望不是 "paused"
如果 animationName 是 none,说明样式根本没匹配上,看第二步;如果有值但画面不动,看第三步。
原因一:动效全写在 hover 里
这是最常见也最容易被忽略的一条。
@media (hover: hover) {
.card:hover {
transform: translateY(-4px);
}
}
触屏设备上 (hover: hover) 永远不匹配,因为手指没有「悬停」这件事。于是你为桌面精心设计的悬浮、流光、缩放,在手机和平板上一次都不会触发。
修复方式:给触屏补 :active 点按反馈,效果不用和 hover 一样,只要让用户知道点到了。
@media (hover: none) {
.card:active {
transform: scale(0.98);
}
.card:active::after {
transform: translateX(130%);
}
}
原因二:用 blur 滤镜做动画
@keyframes charIn {
from { opacity: 0; filter: blur(6px); transform: translateY(0.3em); }
to { opacity: 1; filter: blur(0); transform: translateY(0); }
}
单个元素没问题,但如果这段动画同时作用在几十个字上(逐字入场),iOS 会为每个元素单独建模糊图层,GPU 直接顶不住。表现就是动画卡住不动、或者只播放第一帧。
修复方式:小屏或触屏设备换成不带 blur 的关键帧。
@keyframes charInLite {
from { opacity: 0; transform: translateY(0.3em); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px), (hover: none) {
.char { animation-name: charInLite; }
}
注意断点要用 (hover: none) 兜底,因为 iPad 竖屏宽度有 810px,纯 max-width: 768px 盖不到。
原因三:超大模糊背景层
filter: blur(64px) 加 conic-gradient 再加无限旋转,在桌面上很顺滑,在 A10 这类老芯片上就是灾难。这类装饰层还会和上面的文字动画抢 GPU。
修复方式:降低模糊半径、缩小元素尺寸,并提示浏览器提升为独立合成层。
@media (hover: none) {
.aurora {
inset: -35%;
filter: blur(40px);
}
.orb {
filter: blur(30px);
}
}
.aurora, .orb {
will-change: transform;
}
will-change 不要滥用,只加在确实在动的少数元素上,加多了反而更卡。
原因四:类名没定义
听起来很低级,但真的常见:改版时模板里用了 page-fade-up、quote-hero 这类新类名,CSS 里却忘了写。桌面端因为滚动、hover 的干扰不容易看出来,移动端一看就是「什么动画都没有」。
排查方法很直接,构建产物里搜一下:
grep -o "page-fade-up" out/_next/static/chunks/*.css
搜不到就说明这个类不存在。
原因五:用户开了「减弱动态效果」
iOS 的「设置 → 辅助功能 → 动态效果 → 减弱动态效果」打开后,prefers-reduced-motion: reduce 会生效。如果你的 CSS 里有:
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.01ms !important; }
}
那动画「不动」其实是正确行为,不是 bug。测试前先确认系统设置。
原因六:fill-mode 残留 transform
.animate-card {
animation: fadeUp 0.6s ease-out forwards;
}
forwards 会让动画结束后保留 transform,如果这个盒子里有 position: fixed 的子元素(比如弹窗遮罩),会被这个 transform 困在局部坐标系里,弹窗位置就跑到奇怪的地方。
修复方式:用 backwards 代替 forwards,动画结束回到自然状态;或者把 fixed 元素通过 portal 挂到 body。
排查顺序小结
- 用 Safari 远程调试确认
animationName是否为none - 检查动效是否被包在
@media (hover: hover)里 - 检查关键帧里是否用了
filter: blur() - 检查背景装饰层的模糊半径是否过大
- 检查类名是否真的存在于构建产物
- 检查设备是否开启了减弱动态效果
按这个顺序走,绝大多数「iOS 上动画不动」都能定位。
常见问题
为什么只有 iPad 有问题,iPhone 正常?
iPad 的视口宽度更大,可能落在了不同的媒体查询断点里;另外老款 iPad 的 GPU 性能往往弱于同代 iPhone。
加了 will-change 反而更卡?
will-change 会强制创建合成层,占用显存。只给真正在动的元素加,动画结束后可以移除。
能不能直接在 iOS 上关掉所有动画?
可以,但体验会变差。更好的做法是保留轻量动画(透明度、位移),去掉高开销的部分(模糊、滤镜、大面积渐变)。
Safari 远程调试连不上?
需要 iOS 设备在「设置 → Safari → 高级」里开启 Web 检查器,并用数据线连接,电脑端 Safari 的「开发」菜单里才会出现设备。