先记住这个答案
prefers-reduced-motion 是一个系统级媒体查询,不只影响 transition 和 animation。CSS 里的 scroll-behavior: smooth 也算动效,应该在 reduce 模式下降级为瞬间跳转。JavaScript 动画需要用 window.matchMedia 主动检测,然后决定是跳过动画还是缩短时长。把这三个来源都处理好,才算真正响应了用户的偏好。
- 减弱动效时可关闭平滑滚动
matchMedia可监听偏好变化- 第三方动画库通常有全局配置项可以统一缩短时长
- 用 CSS 自定义属性存时长值,一处改全局生效
用自定义属性统一管理动效时长
项目里动画散落在几十个组件里,逐条覆盖太麻烦。更好的做法是把时长提取成 CSS 自定义属性,在根节点定义默认值,媒体查询里统一改成接近零。
:root {
--duration: 300ms;
--ease: ease;
}
@media (prefers-reduced-motion: reduce) {
:root {
--duration: 0.01s; /* 几乎瞬间,但 transitionend 照常触发 */
--ease: linear;
}
}
.card {
transition: transform var(--duration) var(--ease);
}所有引用 --duration 的组件自动生效,不用写几十条媒体查询覆盖。新增组件时也只要引用同一个变量,减弱动效就自动带上了。
平滑滚动的降级
scroll-behavior: smooth 会让锚点跳转和 scrollIntoView 产生平滑滚动动画。对前庭障碍患者来说,整页的滚动运动和大幅位移动画一样容易引起不适。
在 reduce 模式下应该把它改回默认的 auto,跳转瞬间完成。如果你用 JavaScript 的 scrollIntoView({ behavior: 'smooth' }) 来滚动,也要根据检测结果把 behavior 改成 'auto'。
const prefersReduced = window.matchMedia(
'(prefers-reduced-motion: reduce)'
)
function scrollToSection(el) {
el.scrollIntoView({
behavior: prefersReduced.matches ? 'auto' : 'smooth'
})
}
// 用户在系统设置里切换时实时响应
prefersReduced.addEventListener('change', () => {
// 更新全局配置或重新渲染
})matchMedia 返回的对象可以监听 change 事件,用户在系统偏好里切换时不用刷新页面就能生效。
JavaScript 动画库的处理
用 requestAnimationFrame 手写的动画、Framer Motion、GSAP 这类库不走 CSS 渲染管线,媒体查询写在样式表里对它们没用。需要在 JavaScript 里主动读取 prefersReduced.matches,为 true 时把动画时长设成 0 或直接跳到终态。
Framer Motion 可以在全局的 MotionConfig 上设置 reducedMotion="user",它会自动读取系统偏好。GSAP 没有内置支持,但可以在初始化时根据检测结果设 gsap.defaults({ duration: 0 })。
容易答错的地方
- 以为 CSS 媒体查询能管住所有动画
- CSS 媒体查询只作用于样式表里的声明。JavaScript 用
requestAnimationFrame或element.animate()做的动画完全不受影响,必须在 JavaScript 里单独检测。 - 忘了
scroll-behavior - 很多人答这道题只提到
transition和animation,忘了scroll-behavior: smooth也是动效。面试时能提到滚动降级,回答会更完整。
面试官还会怎么问?
matchMedia 的 change 事件在所有浏览器都支持吗?
现代浏览器都支持 addEventListener('change', ...)。旧的写法是 addListener,已经废弃但仍然能用。如果要兼容 IE,就轮询 matches 属性,不过 IE 本身也不支持 prefers-reduced-motion。
prefers-reduced-motion 和 prefers-reduced-transparency 有什么关系?
两者都是用户偏好媒体查询。prefers-reduced-motion 管动效,prefers-reduced-transparency 管半透明效果。后者浏览器支持较少,目前只有 Safari 实现了。它们互相独立,用户可以只开其中一个。
怎么在开发时测试减弱动效模式?
Chrome DevTools 的 Rendering 面板里可以模拟 prefers-reduced-motion: reduce,不用去系统设置里改。macOS 的路径是「系统设置 - 辅助功能 - 显示 - 减少动态效果」。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。