直接用@media (prefers-reduced-motion: reduce)检测,需同时重置animation、transition、scroll-behavior,并为JS动画手动监听matchMedia变化。

直接用 @media (prefers-reduced-motion: reduce) 就能检测,不需要 JS 监听——这是浏览器原生支持的 CSS 媒体查询,开箱即用。
为什么不能只写 animation: none 就完事?
很多人加了媒体查询后只禁关键帧动画,结果 hover 下拉菜单卡住、模态框不聚焦、滚动条突然失灵。问题出在:过渡(transition)、平滑滚动(scroll-behavior)、甚至某些 transform 和 opacity 的 inline 变化,都会在 reduced 模式下继续插值,造成布局错乱或焦点丢失。
- 必须同时重置
transition、animation、scroll-behavior - 对依赖动画逻辑的 JS 组件(比如轮播图、抽屉菜单),CSS 层面禁用后,JS 仍可能尝试执行 rAF 动画,需额外降级
- 别用
animation-duration: 0s,它仍会触发解析和计时器;animation: none才真正跳过整个动画流程
怎么批量关闭所有 CSS 动画和过渡?
最稳妥的兜底写法是全局重置,再按需放开极简反馈:
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
scroll-behavior: auto !important;
}
/* 允许颜色类变化,保留最小可用性反馈 */
[class*="bg-"], [class*="text-"] {
transition: background-color 0.05s, color 0.05s !important;
}
}
-
!important是必须的——第三方组件或内联样式常带高优先级,不加容易被覆盖 - 不要用
* { animation-duration: 0.01ms }这类“伪禁用”,它仍会触发渲染管线,低端机上反而更卡 - 如果用了自定义滚动条(
::-webkit-scrollbar),也要单独加transition: none,Safari 在 reduced 模式下不会自动静音它
如何让 JS 动画也响应这个偏好?
CSS 媒体查询管不到 JS,所以 requestAnimationFrame、GSAP、或者 element.animate() 都得手动判断:
const motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
function startAnimation(el) {
if (motionQuery.matches) {
el.style.opacity = '1';
el.style.transform = 'none';
return;
}
el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 300 });
}
- 用
window.matchMedia而不是getComputedStyle查媒体查询状态,后者无法读取动态变化 - 监听
motionQuery.addEventListener('change', handler),避免用户中途切换系统设置后 JS 动画失效 - 对骨架屏、加载指示器这类纯视觉组件,直接提供静态 fallback(如一张占位图 + 文字提示),比硬切 JS 状态更可靠
真正难的不是写那几行 CSS,而是想清楚哪些动效是「结构必需」(比如菜单展开时 height 过渡保障可滚动性),哪些是「纯视觉装饰」(比如云朵飘动背景)。前者要降级为瞬时切换+逻辑补全,后者才该彻底关闭。否则,关掉动画反而让用户更难完成操作。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











