首选减压:有时运动越少

prefers-reduced-motion 媒体查询用于检测用户是否已请求操作系统尽量减少所使用的动画或动作量。

并非所有用户都喜欢装饰性动画或转场效果,有些用户在看到视差滚动、缩放效果等时会直接感到晕动。借助用户偏好媒体查询 prefers-reduced-motion,您可以为已表达此偏好的用户设计减少动画效果的网站变体。

浏览器支持

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

来源

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

前几天,我和孩子们一起滑冰。那天天气晴朗,阳光明媚,溜冰场挤满了人 ⛸。唯一的问题是:我不太擅长应付人群。有这么多移动目标,我无法集中注意力,最终迷失了方向,感觉视觉信息完全超载,就像盯着蚁丘 🐜?。

一群滑冰者的脚。
现实生活中的视觉信息过载。

有时,网络上也会出现同样的情况:闪烁的广告、花哨的视差效果、令人惊讶的揭示动画、自动播放的视频等等,网络有时会让人感到非常繁杂。但值得庆幸的是,与现实生活中不同,我们可以找到解决方案。借助 CSS 媒体查询 prefers-reduced-motion,开发者可以为偏好减少动画的用户创建网页变体。这可以是任何内容,从禁止自动播放视频到停用某些纯装饰性效果,再到为特定用户完全重新设计页面。

在深入了解该功能之前,我先回顾一下 Web 上动画的用途。如有需要,您也可以跳过背景信息,直接进入技术细节

网页上的动画

动画通常用于向用户提供反馈,例如告知用户系统已收到操作并正在处理。例如,在购物网站上,可以为产品添加动画效果,使其“飞入”虚拟购物车(以网站右上角的图标表示)。

另一种用例涉及使用动作来操控用户感知,具体方法是混合使用骨架屏幕、情境元数据和低质量图片预览,占用用户大量时间,让整个体验感觉更快。其目的是向用户提供相关背景信息,让他们了解接下来会发生什么,同时尽快加载内容。

最后,还有装饰效果,例如动画渐变、视差滚动、背景视频等。虽然许多用户喜欢此类动画,但有些用户却不喜欢,因为它们会分散用户注意力或拖慢系统速度。在最糟糕的情况下,用户甚至可能会出现与现实生活中类似的晕动症,因此,对于这类用户,减少动画是医学上的必要措施

由运动触发的耳石谱系障碍

有些用户会因动画内容而分心或感到恶心。例如,当与滚动相关联的主要元素以外的元素大量移动时,滚动动画可能会导致前庭功能障碍。例如,由于背景元素的移动速度与前景元素不同,因此视差滚动动画可能会导致前庭系统失调。前庭(内耳)疾病反应包括头晕、恶心和偏头痛,有时需要卧床休息才能康复。

在操作系统上移除动画

许多操作系统都提供了无障碍功能设置,可用于指定减少动画的偏好设置。以下屏幕截图显示了 macOS Mojave 的减少动作偏好设置和 Android Pie 的移除动画偏好设置。选中这些偏好设置后,操作系统将不会使用应用启动动画等装饰效果。应用本身也可以且应遵循此设置,并移除所有不必要的动画。

已选中“减少动作”复选框的 macOS 设置屏幕。
Android 设置屏幕,其中“移除动画”复选框处于选中状态。

在网页上移除动态效果

媒体查询级别 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 状态猫创建了一个小演示。首先,请花点时间欣赏这个笑话,它非常有趣,我会等您。现在,我来介绍一下演示。滚动时,每个 HTTP 状态类别都会交替显示在右侧或左侧。这是一个流畅的 60 FPS 动画,但正如前面所述,有些用户可能不喜欢它,甚至会因它而感到晕动,因此该演示程序会遵循 prefers-reduced-motion 进行编程。这甚至可以动态运行,因此用户可以动态更改其偏好设置,而无需重新加载。如果用户偏好减少动画,系统会移除不必要的展开动画,只保留常规滚动动画。以下屏幕录制内容演示了演示的实际运作:

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 启动的动作),因此,如果您发现动作中断,请务必停用此方法。

致谢

非常感谢 Stephen McGruer 在 Chrome 中实现了 prefers-reduced-motion,并与 Rob Dodson 一起审核了这份文档。主打图片:Unsplash 上的 Hannah Cauhepe。