prefers-reduced-motion 媒体查询用于检测用户是否已请求操作系统尽量减少所使用的动画或动作量。
并非所有用户都喜欢装饰性动画或转场效果,有些用户在看到视差滚动、缩放效果等时会直接感到头晕。借助用户偏好设置媒体查询 prefers-reduced-motion
,您可以为已表现出这种偏好设置的用户设计出网站减少动作的变体。
现实生活中和网络上动作过多
前几天,我和孩子们一起去滑冰。那天天气晴朗,阳光明媚,溜冰场挤满了人 ⛸。唯一的问题是:我不太擅长应付人群。有这么多移动目标,我无法集中注意力,最终迷失了方向,感觉视觉信息完全过载,就像盯着蚁丘 🐜。
有时,网络上也会出现同样的情况:闪烁的广告、花哨的视差效果、令人惊讶的揭示动画、自动播放的视频等等,网络有时会让人感到非常繁杂。但幸运的是,与现实生活中不同,我们可以找到解决方案。借助 CSS 媒体查询 prefers-reduced-motion
,开发者可以为偏好减少动画的用户创建网页变体。这可以包括任何内容,从禁止自动播放视频到停用某些纯装饰性效果,再到为特定用户完全重新设计页面。
在深入了解该功能之前,我先回顾一下 Web 上动画的用途。如有需要,您也可以跳过背景信息,直接进入技术细节。
网页上的动画
动画通常用于向用户提供反馈,例如告知用户系统已收到操作并正在处理。例如,在购物网站上,可通过动画将产品“飞”入虚拟购物车,并以图标的形式显示在网站右上角。
另一种用例涉及使用动作来操控用户感知,具体方法是混合使用骨架屏幕、情境元数据和低质量图片预览,占用用户大量时间,让整个体验感觉更快。其理念是向用户提供即将发生的背景信息,同时尽快加载内容。
最后,还有装饰效果,例如动画渐变、视差滚动、背景视频等。虽然许多用户喜欢此类动画,但有些用户不喜欢此类动画,因为它们感觉分心或拖慢了速度。在最糟糕的情况下,用户甚至可能会像在现实生活中一样遭受晕动病的折磨,因此对于这些用户来说,减少动画是一种必要的医疗手段。
运动触发的前庭声谱障碍
有些用户会因动画内容而分心或感到恶心。例如,当与滚动相关联的主要元素以外的元素大量移动时,滚动动画可能会导致前庭障碍。例如,由于背景元素的移动速度与前景元素不同,因此视差滚动动画可能会导致前庭系统失调。前庭(内耳)疾病反应包括头晕、恶心和偏头痛,有时需要卧床休息才能康复。
在操作系统上移除动态效果
许多操作系统都提供了无障碍功能设置,可用于指定减少动画的偏好设置。以下屏幕截图显示了 macOS Mojave 的 Reduce motion 偏好设置和 Android Pie 的 Remove animation 偏好设置。选中这些偏好设置后,操作系统将不会使用应用启动动画等装饰效果。应用本身也可以且应遵循此设置,并移除所有不必要的动画。
移除网页上的动画
媒体查询级别 5 还将减少动画的用户偏好设置引入到 Web 中。借助媒体查询,作者可以独立于要呈现的文档来测试和查询用户代理或显示设备的值或功能。媒体查询 prefers-reduced-motion
用于检测用户是否已设置操作系统偏好设置,以尽量减少其使用的动画或动作量。它可能有两个可能的值:
no-preference
:表示用户在底层操作系统中没有偏好。此关键字值在布尔上下文中会评估为false
。reduce
:表示用户已设置操作系统偏好设置,指示界面应尽可能减少移动或动画,最好是移除所有非必要移动。
处理来自 CSS 和 JavaScript 上下文的媒体查询
与所有媒体查询一样,prefers-reduced-motion
可从 CSS 上下文和 JavaScript 上下文进行检查。
为了说明这两者,假设我有一个重要的注册按钮,希望用户点击。我可以定义一个引人注目的“振动”动画,但作为一名优秀的 Web 公民,我只会向明确表示可以接受动画的用户播放动画,而不会向其他用户播放,这类用户包括已选择停用动画的用户,或使用不支持媒体查询的浏览器的用户。
/*
If the user has expressed their preference for
reduced motion, then don't use animations on buttons.
*/
@media (prefers-reduced-motion: reduce) {
button {
animation: none;
}
}
/*
If the browser understands the media query and the user
explicitly hasn't set a preference, then use animations on buttons.
*/
@media (prefers-reduced-motion: no-preference) {
button {
/* `vibrate` keyframes are defined elsewhere */
animation: vibrate 0.3s linear infinite both;
}
}
为了说明如何通过 JavaScript 使用 prefers-reduced-motion
,假设我使用 Web Animations API 定义了一个复杂动画。虽然在用户偏好设置发生变化时,浏览器会动态触发 CSS 规则,但对于 JavaScript 动画,我必须自行监听更改,然后手动停止可能正在执行的动画(如果用户允许,则可以重启这些动画):
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
mediaQuery.addEventListener('change', () => {
console.log(mediaQuery.media, mediaQuery.matches);
// Stop JavaScript-based animations.
});
请注意,实际媒体查询必须用括号括起来:
window.matchMedia('prefers-reduced-motion: reduce');
window.matchMedia('(prefers-reduced-motion: reduce)');
处理来自 <picture>
上下文的媒体查询
一个有趣的用例是使动画 AVIF、WebP 或 GIF 的播放依赖于 media
属性。如果 (prefers-reduced-motion: no-preference)
的计算结果为 true
,则可以安全地显示动画版本,否则显示静态版本:
<picture>
<!-- Animated versions. -->
<source
srcset="nyancat.avifs"
type="image/avif"
media="(prefers-reduced-motion: no-preference)"
/>
<source
srcset="nyancat.gif"
type="image/gif"
media="(prefers-reduced-motion: no-preference)"
/>
<!-- Static versions. -->
<img src="nyancat.png" alt="Nyan cat" width="250" height="250" />
</picture>
您可以参阅以下示例。请尝试切换设备的动作偏好设置,看看有什么不同。
在请求时发现用户的偏好设置
借助 Sec-CH-Prefers-Reduced-Motion
客户端提示标头,网站可以选择在请求时获取用户的动作偏好设置,以便服务器出于性能方面的原因内嵌正确的 CSS。
演示
我根据 Rogério Vicente 的精彩 🐈? HTTP status cats 创建了一个小演示。首先,请花点时间欣赏这个笑话,它非常有趣,我会等待。现在您回来了,我来介绍一下演示。在滚动时,每个 HTTP 状态猫会从右侧或左侧交替显示。这是一个非常流畅的 60 FPS 动画,但如前所述,有些用户可能不喜欢它,甚至会因此产生晕动感,因此该演示被编程为遵循 prefers-reduced-motion
。这甚至可以动态运行,因此用户可以即时更改其偏好设置,而无需重新加载。如果用户更喜欢减少动作,不必要的揭示动画将消失,只留下常规的滚动动作。以下屏幕录制内容演示了该演示的实际运作:
总结
尊重用户偏好设置对现代网站来说至关重要,浏览器为了支持 Web 开发者而推出越来越多的功能。另一个已发布的示例是 prefers-color-scheme
,它可检测用户是偏爱浅色还是深色配色方案。您可以参阅我的文章《Hello Darkness, My Old Friend》 🌒,详细了解 prefers-color-scheme
。
CSS 工作组正在标准化更多用户偏好媒体查询,例如 prefers-reduced-transparency
(检测用户是否更喜欢降低透明度)、prefers-contrast
(检测用户是否请求系统增加或减少相邻颜色之间的对比度)和 inverted-colors
(检测用户是否更喜欢反相颜色)。
(额外提示)强制在所有网站上减少动画效果
并非所有网站都会使用 prefers-reduced-motion
,或者可能不符合您的偏好。
如果出于任何原因,您确实想在所有网站上设置定格动画,您完全可以。实现此目的的一种方法是,向您访问的每个网页中注入包含以下 CSS 的样式表。有几个浏览器扩展程序(使用时需自担风险!)可以实现此目的。
@media (prefers-reduced-motion: reduce) {
*,
::before,
::after {
animation-delay: -1ms !important;
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
background-attachment: initial !important;
scroll-behavior: auto !important;
transition-duration: 1ms !important;
transition-delay: -1ms !important;
}
}
其运作方式是,之前的 CSS 会覆盖所有动画和过渡的时长,使其不再被注意到的短时间。由于某些网站需要运行动画才能正常运行(或许是因为某个步骤取决于 animationend
事件的触发),因此更极端的 animation: none !important;
方法将不起作用。即使之前的解决方法也不保证在所有网站上都能成功(例如,它无法停止使用 Web Animations API 启动的动作),因此请务必在发现问题时停用该方法。
相关链接
- 媒体查询第 5 级规范的最新编辑者草稿。
prefers-reduced-motion
(针对 Chrome 平台状态)。prefers-reduced-motion
Chromium 错误。- Blink 意图实现发布功能。
致谢
非常感谢 Stephen McGruer 在 Chrome 中实现了 prefers-reduced-motion
,并与 Rob Dodson 一起审核了这份文档。主打图片:Unsplash 上的 Hannah Cauhepe。