直接写 animation: none 不生效,因其仅清空当前声明,无法覆盖父级、inline style 或第三方样式;须用 @media (prefers-reduced-motion: reduce) 高权重重置,并补全终态样式如 transform: none !important、opacity: 1 !important。

为什么直接写 animation: none 不生效
很多人在 @media (max-width: 480px) 里加 .card { animation: none; },结果动画照常跑——因为 animation: none 只清空当前声明,无法覆盖父级样式、style 属性或第三方组件注入的 animation 值。更关键的是:它不处理隐式过渡(比如 JS 直接改 transform 或 opacity,浏览器仍会自动插值)。
必须用 @media (prefers-reduced-motion: reduce)
这不是“低端设备专属 hack”,而是操作系统级信号:iOS 低电量模式、Android 辅助功能开关、Windows/macOS 可访问性设置都会触发它。所有现代浏览器(Chrome/Firefox/Safari/Edge)原生支持,且比 UA 判断或 deviceMemory 更可靠。
-
@media (prefers-reduced-motion: true)是无效语法,整个块会被忽略;只能用reduce或no-preference - 要同时重置
animation、transition、scroll-behavior,否则下拉菜单闪开、滚动失灵 - 必须补终态样式:
transform: none !important、opacity: 1 !important,否则元素卡在中间位置
别只靠 CSS,JS 动画也得同步降级
CSS 媒体查询管不到 requestAnimationFrame、GSAP、轮播组件或骨架屏。必须手动监听并响应:
- 初始化时读取:
const motionQuery = window.matchMedia("(prefers-reduced-motion: reduce)") - 监听变化:
motionQuery.addEventListener("change", () => document.documentElement.classList.toggle("reduced-motion", motionQuery.matches)) - 所有 JS 动画启动前加判断:
if (!motionQuery.matches) { startParallax() } - 对加载类动画(如骨架屏),直接设终态:
el.style.opacity = "1"; el.style.transform = "none"
老旧安卓 WebView 需额外兜底
Safari 13+/Chrome 74+ 支持 prefers-reduced-motion,但 Android 4.4–6 的系统 WebView 完全不识别。这时需 JS 辅助判断:
立即学习“前端免费学习笔记(深入)”;
- 组合条件:
matchMedia("(max-width: 480px) and (resolution: 1dppx)").matches+ UA 中含WebView或Android 4 - 命中后给
加class="no-animation",CSS 中用.no-animation .card { animation-duration: 0.01ms !important; } - 禁用
will-change: transform和transform-style: preserve-3d,它们在老 WebKit 里极易白屏
最容易被忽略的是:动画禁用后,按钮点击态、加载指示器可能彻底消失。得把“动效逻辑”和“状态逻辑”拆开——前者负责过渡,后者负责终态呈现。



















