prefers-reduced-motion:有时少即是多

prefers-reduced-motion 媒体查询检测用户是否已请求操作系统最大限度地减少其使用的动画或动作量。

并非所有人都喜欢装饰性动画或过渡效果,有些用户在面对视差滚动、缩放效果等时,会直接感到晕动症。用户偏好媒体查询 prefers-reduced-motion 让您可以为表达此偏好的用户设计一个动作减少的网站变体。

浏览器支持

  • Chrome: 74.
  • Edge: 79.
  • Firefox: 63.
  • Safari: 10.1.

来源

现实生活和网络上过多的动作

前几天,我和孩子们一起去滑冰。那天阳光明媚,溜冰场挤满了人 ⛸。唯一的问题是:我不太能应付人群。面对如此多的移动目标,我无法专注于任何事物,最终迷失方向,并感到完全的视觉超载,几乎就像盯着蚁丘 🐜 一样。

Throng of feet of ice skating people.
现实生活中的视觉超载。

有时,同样的情况也会发生在网络上:闪烁的广告、花哨的视差效果、令人惊讶的显示动画、自动播放的视频等等,网络有时可能非常令人感到不知所措……幸运的是,与现实生活不同,有一种解决方案。CSS 媒体查询 prefers-reduced-motion 让开发者可以为偏好减少动作的用户创建页面变体。这可以包括从避免自动播放视频到禁用某些纯粹的装饰效果,再到为某些用户完全重新设计页面。

在深入探讨该功能之前,让我退后一步,思考一下动画在网络上的用途。如果您愿意,也可以跳过背景信息,直接跳转到技术细节

网络上的动画

动画通常用于向用户提供反馈,例如,让他们知道已收到操作并且正在处理中。例如,在购物网站上,可以将产品动画化为“飞入”虚拟购物车,该购物车被描绘为网站右上角的图标。

另一个用例是使用动作来 利用用户感知,通过混合使用骨架屏、上下文元数据和低质量图像预览来占用用户的大量时间,并使整个体验感觉更快。其理念是向用户提供即将发生的事情的上下文,同时尽可能快地加载内容。

最后,还有装饰性效果,如动画渐变、视差滚动、背景视频等。虽然许多用户喜欢这些动画,但有些用户不喜欢它们,因为他们感到分心或被它们拖慢了速度。在最坏的情况下,用户甚至可能会像现实生活体验一样患上晕动症,因此对于这些用户来说,减少动画是一种医疗必需

动作触发的前庭频谱障碍

有些用户会因动画内容而分心或恶心。例如,当滚动动画中,与滚动关联的主要元素以外的元素移动过多时,可能会导致前庭障碍。例如,视差滚动动画可能会导致前庭障碍,因为背景元素的移动速度与前景元素不同。前庭(内耳)障碍反应包括头晕、恶心和偏头痛,有时需要卧床休息才能恢复。

移除操作系统上的动作

长期以来,许多操作系统都具有用于指定减少动作偏好的辅助功能设置。以下屏幕截图显示了 macOS Mojave 的减少动作偏好设置和 Android Pie 的移除动画偏好设置。选中后,这些偏好设置会导致操作系统不使用装饰性效果,如应用程序启动动画。应用程序本身也可以并且应该遵守此设置,并移除所有不必要的动画。

The macOS settings screen with the 'Reduce motion' checkbox checked.
The Android settings screen with the 'Remove animations' checkbox checked.

移除网络上的动作

媒体查询级别 5 也将减少动作的用户偏好引入了网络。媒体查询允许作者测试和查询用户代理或显示设备的值或功能,而无需考虑正在呈现的文档。媒体查询 prefers-reduced-motion 用于检测用户是否已设置操作系统偏好,以最大限度地减少其使用的动画或动作量。它可以采用两个可能的值:

  • no-preference:表示用户在底层操作系统中未做出任何偏好设置。此关键字值在布尔上下文中评估为 false
  • reduce:表示用户已设置操作系统偏好,指示界面应最大限度地减少移动或动画,最好达到移除所有非必要移动的程度。

在 CSS 和 JavaScript 上下文中使用媒体查询

与所有媒体查询一样,可以从 CSS 上下文和 JavaScript 上下文检查 prefers-reduced-motion

为了说明这两者,假设我有一个重要的注册按钮,我希望用户点击。我可以定义一个引人注目的“振动”动画,但作为一名优秀的网络公民,我只会为那些明确表示可以接受动画的用户播放它,而不会为其他所有人播放,这些人可能是选择退出动画的用户,或者浏览器不理解媒体查询的用户。

/*
  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>

您可以看到以下示例。尝试切换设备的动作偏好设置以查看差异。

The famous Nyan cat.

在请求时发现用户的偏好

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 发起的动作),因此请务必在发现损坏时停用它。

致谢

特别感谢 Stephen McGruer,他在 Chrome 中实现了 prefers-reduced-motion,并与 Rob Dodson 一起审阅了本文档。英雄图片由 Hannah Cauhepe 在 Unsplash 上提供。