prefers-reduced-motion 媒体查询用于检测用户是否已请求操作系统尽量减少动画或动态效果的使用量。
并非所有人都喜欢装饰性动画或过渡效果,有些用户在遇到视差滚动、缩放效果等时甚至会感到晕动症。用户偏好设置媒体查询 prefers-reduced-motion 可让您为表达过此偏好的用户设计一个减少动作的网站变体。
现实生活和网络上的动态效果过多
前几天,我和孩子们一起去滑冰。那天天气很好,阳光明媚,溜冰场里挤满了人 ⛸。但问题是,我不太能应付拥挤的人群。由于目标太多,我无法专注于任何事情,最终会感到迷茫,并且视觉信息过载,几乎就像盯着蚁丘 🐜 一样。
有时,网络上也会出现类似情况:闪烁的广告、精美的视差效果、令人惊喜的揭示动画、自动播放的视频等等,有时网络会让人感到不知所措…不过,与现实生活不同的是,这种情况有解决方案。借助 CSS 媒体查询 prefers-reduced-motion,开发者可以为偏好减少动画效果的用户创建网页变体。这可能包括各种措施,例如避免使用自动播放的视频、停用某些纯装饰性效果,甚至针对特定用户完全重新设计网页。
在深入介绍该功能之前,我们先退一步,思考一下动画在网页上的用途。如果您愿意,也可以跳过背景信息,直接了解技术细节。
网页上的动画
动画通常用于向用户提供反馈,例如,告知用户系统已收到某项操作并正在处理。例如,在购物网站上,商品可以以动画形式“飞入”虚拟购物车,该购物车显示为网站右上角的图标。
另一个用例涉及使用动态来欺骗用户感知,具体方法是混合使用骨架屏、情境元数据和低质量图片预览来占用用户的大量时间,并让整个体验感觉更快。这样做的目的是让用户了解即将发生的事情,同时尽可能快速地加载内容。
最后,还有一些装饰性效果,例如动画渐变、视差滚动、背景视频等。虽然许多用户喜欢此类动画,但有些用户不喜欢,因为他们觉得动画会让他们分心或减慢他们的速度。在最糟糕的情况下,用户甚至可能会像在现实生活中一样患上晕动症,因此对于这些用户来说,减少动画是医学上的必需品。
运动触发的前庭谱系障碍
部分用户会因动画内容而感到分心或恶心。例如,当与滚动关联的主要元素以外的元素四处移动时,滚动动画可能会导致前庭障碍。例如,视差滚动动画可能会导致前庭障碍,因为背景元素的移动速度与前景元素不同。前庭(内耳)障碍反应包括头晕、恶心和偏头痛,有时需要卧床休息才能恢复。
在操作系统中移除运动
许多操作系统长期以来都提供辅助功能设置,用于指定对减少动作的偏好。以下屏幕截图显示了 macOS Mojave 的减少动画效果偏好设置和 Android Pie 的移除动画效果偏好设置。选中后,这些偏好设置会导致操作系统不使用装饰性效果,例如应用启动动画。应用本身也可以且应该遵循此设置,并移除所有不必要的动画。
在网页上移除动态效果
媒体查询级别 5 也将减少运动的用户偏好设置引入了 Web。媒体查询允许作者测试和查询用户代理或显示设备的值或功能,而无需考虑正在呈现的文档。媒体查询 prefers-reduced-motion 用于检测用户是否已设置操作系统偏好设置,以尽可能减少使用的动画或动态效果量。它可以采用两个可能的值:
no-preference:表示用户在底层操作系统中未设置偏好。此关键字值在布尔值上下文中评估为false。reduce:表示用户已设置操作系统偏好设置,指示界面应尽可能减少移动或动画,最好是移除所有非必要的移动。
在 CSS 和 JavaScript 环境中使用媒体查询
与所有媒体查询一样,prefers-reduced-motion 可从 CSS 上下文和 JavaScript 上下文进行检查。
为了说明这两种情况,假设我有一个重要的注册按钮,希望用户点击该按钮。我可以定义一个引人注目的“振动”动画,但作为一名优秀的网络公民,我只会向明确同意使用动画的用户播放该动画,而不会向其他用户播放,这些用户可能已选择停用动画,或者使用的是不支持该媒体查询的浏览器。
/*
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 状态猫创建了一个小演示。首先,请花点时间欣赏一下这个笑话,它非常搞笑,我会等您。现在您回来了,让我来介绍一下演示。滚动时,每个 HTTP 状态猫会交替从右侧或左侧出现。这是一个非常流畅的 60 FPS 动画,但如前所述,部分用户可能不喜欢它,甚至会因此而晕动症,因此该演示程序被编程为尊重 prefers-reduced-motion。此功能甚至可以动态运行,因此用户可以随时更改偏好设置,无需重新加载。如果用户偏好减少动态效果,则非必要的显示动画会消失,只剩下常规的滚动动态效果。以下屏幕截图展示了演示的实际效果:
prefers-reduced-motion 演示应用视频
总结
尊重用户偏好对于现代网站至关重要,浏览器正在公开越来越多的功能,以便 Web 开发者能够做到这一点。另一个已发布的功能示例是 prefers-color-scheme,它可以检测用户是偏好浅色还是深色配色方案。您可以在我的文章你好,黑暗,我的老朋友 🌒 中详细了解 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-motionChrome 平台状态。prefers-reduced-motionChromium bug。- Blink “Intent to Implement”帖子。
致谢
特别感谢 Stephen McGruer 在 Chrome 中实现了 prefers-reduced-motion,并与 Rob Dodson 一起审核了本文档。