Reduce Motion — 操作系统和浏览器的无障碍功能,用于关闭或减少界面动画,以防止前庭障碍用户感到不适。在 iOS 中,该设置位于 Settings > Accessibility > Motion > Reduce Motion,可关闭视差、屏幕之间过渡动画和图标效果。在 Android 中,类似的“删除动画“选项位于设置 > 无障碍。根据 W3C WCAG 2.2 的数据,过度动画可能导致 15–35% 的前庭障碍患者出现恶心、头晕和偏头痛。
要点
Reduce Motion — 一种系统设置,指示操作系统、浏览器和应用程序最小化或完全关闭动画、运动、缩放和视差效果。该功能面向前庭障碍患者,界面中的运动元素会导致他们出现恶心、头晕、迷失方向和偏头痛。
内耳的前庭系统负责平衡感和空间定向。当眼睛看到屏幕上的运动,但前庭器官没有感受到身体的相应运动时,就会产生感觉冲突。这种冲突会触发自主神经反应:恶心、冷汗、头晕。根据前庭障碍协会的数据,约 35% 的 40 岁以上人群因界面中的运动效果而感到不适。
Reduce Motion 与简单关闭动画的关键区别 — 系统级别。已启用的设置通过操作系统 API 或 CSS 媒体功能 prefers-reduced-motion 传递给所有应用程序和网站。开发者无需猜测用户是否需要动画 — 系统会告知。这是渐进增强方法的一部分:动画是增强,而非强制元素。
Reduce Motion 影响视差效果(设备倾斜时背景移动)、屏幕之间过渡动画(push、slide、fade)、点击时图标缩放、消息效果(SMS 气泡)、加载动画(spinner、skeleton、progress bar animation)、游戏中的视差和 Web 视差滚动效果。静态图像和视频不受影响。
| 动画类型 | iOS Reduce Motion | Android Remove Animation | CSS prefers-reduced-motion |
|---|---|---|---|
| 壁纸视差 | 关闭 | 关闭 | 无 |
| 屏幕之间的过渡 | 替换为 fade | 关闭 | CSS transitions |
| 图标动画 | 关闭 | 不影响 | CSS animations |
| 加载微调器 | 不影响 | 关闭 | CSS animations |
| Skeleton 屏幕 | 不影响 | 替换为静态 | CSS animations |
Reduce Motion 的主要受众 — 前庭障碍(梅尼埃病、迷路炎、BPPV)、偏头痛(尤其是先兆型)、晕动症(交通工具中的运动病)和某些形式的癫痫患者。该功能对老年用户也有用 — 前庭器官的年龄相关变化会增加对运动的敏感度,对 ADHD 患者也是如此 — 移动元素会分散阅读注意力。
在 iOS 中,Reduce Motion 位于 Settings > Accessibility > Motion,包含三个选项:Reduce Motion(主开关)、Auto-Play Message Effects(自动播放 iMessage 效果)和 Auto-Play Video Previews(App Store 中视频的自动预览)。Reduce Motion 最强大:关闭壁纸视差、应用程序打开动画(缩放)、屏幕之间的过渡(push 替换为 cross-fade)以及主屏幕上的图标效果。
从技术上讲,iOS 在 UIKit 级别通过 UIView animations 和 UIViewControllerAnimatedTransitioning 实现 Reduce Motion。当设置启用时,持续时间 > 0 的 UIView.animate 缩短至 0.01 秒,UIViewController.transitionDuration 返回 0。对于视差,iOS 在 UIWindow 上关闭 UIInterpolatingMotionEffect。开发者可以通过 UIAccessibility.isReduceMotionEnabled 检查状态。
开发者可以在 iOS 中为 Reduce Motion 调整动画:用 fade 效果替换复杂过渡,在自定义 UI 组件中关闭视差,以及缩短动画持续时间。SwiftUI 在 reduceMotion 激活时提供特殊的 .animation(nil) 修饰符。
import UIKit
class MotionManager {
// 检查 Reduce Motion 状态
static var isReduceMotionEnabled: Bool {
return UIAccessibility.isReduceMotionEnabled
}
// 考虑 Reduce Motion 的安全动画
static func animate(
duration: TimeInterval,
delay: TimeInterval = 0,
options: UIView.AnimationOptions = [],
animations: @escaping () -> Void,
completion: ((Bool) -> Void)? = nil
) {
let adjustedDuration = isReduceMotionEnabled
? 0.01
: max(duration, 0.01)
UIView.animate(
withDuration: adjustedDuration,
delay: delay,
options: options,
animations: animations,
completion: completion
)
}
}
// SwiftUI — Reduce Motion 的修饰符
import SwiftUI
struct MotionAwareView: View {
@Environment(\.accessibilityReduceMotion)
private var reduceMotion
@State private var isAnimating = false
var body: some View {
Circle()
.scaleEffect(isAnimating ? 1.2 : 1.0)
.animation(
reduceMotion
? nil
: .easeInOut(duration: 1.0).repeatForever(),
value: isAnimating
)
.onAppear { isAnimating = true }
}
}
// 订阅 Reduce Motion 变化
NotificationCenter.default.addObserver(
self,
selector: #selector(motionStatusChanged),
name: UIAccessibility.reduceMotionStatusDidChangeNotification,
object: nil
)MotionManager 类检查 isReduceMotionEnabled 并调整动画持续时间:当 Reduce Motion 激活时,持续时间缩短至 0.01 秒(实际为立即)。SwiftUI 的 .animation(nil) 修饰符在 reduceMotion == true 时完全关闭动画。订阅 reduceMotionStatusDidChangeNotification 允许在不重新加载应用程序的情况下动态切换动画。
iOS 的一个单独选项 — Auto-Play Message Effects — 控制 iMessage 中效果的播放:气球动画、五彩纸屑、激光和烟花。当 Reduce Motion 启用时,iMessage 效果自动关闭。开发者可以通过 UIAccessibility.isReduceMotionEnabled 检查此选项 — Message Effects 没有单独的 API,它们与通用设置绑定。
在 Android 中,”删除动画“设置位于两个位置:设置 > 无障碍 > 删除动画(用于无障碍设置)和开发者选项 > 动画缩放(面向开发者)。无障碍版本关闭所有系统动画,包括屏幕之间的过渡、应用程序打开动画和加载效果。开发者选项允许将缩放比例降低至 0.5x、0.1x 或完全关闭。
从技术上讲,Android 通过系统属性实现动画关闭:window_animation_scale、transition_animation_scale 和 animator_duration_scale。在无障碍设置下,三者均为 0。系统启动器和系统应用程序检查这些值。对于第三方应用,Android 推荐使用 Jetpack Compose 和 View system,它们通过 AnimationScaleObserver 自动读取系统设置。
开发者可以通过 Settings.Global 检查 Android 中的动画状态并调整 UI。Jetpack Compose 提供 AnimationScaleObserver 用于自动调整。关键值 — animator_duration_scale:如果为 0,动画已完全关闭。
import android.content.Context
import android.provider.Settings
import android.database.ContentObserver
import android.os.Handler
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.runtime.getValue
class AndroidMotionHelper {
// 检查动画关闭状态
fun isAnimationDisabled(context: Context): Boolean {
return try {
Settings.Global.getFloat(
context.contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE
) == 0f
} catch (e: Settings.SettingNotFoundException) {
false
}
}
// 观察动画变化(ContentObserver)
fun observeAnimationScale(
context: Context,
onChange: (Boolean) -> Unit
): ContentObserver {
val observer = object : ContentObserver(Handler()) {
override fun onChange(selfChange: Boolean) {
onChange(isAnimationDisabled(context))
}
}
context.contentResolver.registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.ANIMATOR_DURATION_SCALE
),
false,
observer
)
return observer
}
}
// Jetpack Compose — 考虑设置的动画
@Composable
fun MotionAwareAnimation(enabled: Boolean) {
val context = androidx.compose.ui.platform.LocalContext.current
val noMotion = AndroidMotionHelper().isAnimationDisabled(context)
val scale by animateFloatAsState(
targetValue = if (enabled) 1f else 0f,
animationSpec = if (noMotion) {
androidx.compose.animation.core.tween(durationMillis = 0)
} else {
androidx.compose.animation.core.spring()
},
label = "scale"
)
// 带有动画的 UI
}
AndroidMotionHelper 类通过 Settings.Global 检查 ANIMATOR_DURATION_SCALE,提供 ContentObserver 跟踪变化,并允许 Jetpack Compose 动画进行调整:当动画关闭时,durationMillis = 0(立即应用)。对于 View system,请使用 ViewPropertyAnimator.withLayer() 并在启动动画前检查 isAnimationDisabled()。
Android 中的“删除动画“无障碍设置 — 普通用户关闭动画的唯一方式。开发者选项仅面向开发者,可能被隐藏。无障碍设置保证通过系统 API 传递给所有应用程序。开发者应依赖 AccessibilityManager,而非开发者选项。
CSS 媒体功能 prefers-reduced-motion 允许 Web 开发者在用户在操作系统中启用 Reduce Motion 时关闭或替换动画。浏览器在系统设置激活时传递 reduce 值,如果设置未更改则传递 no-preference。自 2020 年起所有现代浏览器均支持:Chrome 74+、Safari 10.3+、Firefox 64+、Edge 79+。
MDN 和 W3C 建议 — 始终将 prefers-reduced-motion: reduce 与动画一起设置。启用了 Reduce Motion 的用户不应看到脉冲、视差、轮播自动播放和 skeleton 加载。可以显示静态内容替代动画,或使用最小持续时间的 CSS transition。以下是完整适配的示例。
在 prefers-reduced-motion: reduce 情况下,网页应:关闭 CSS 动画(@keyframes)、将 CSS transitions 替换为即时更改、停止 JavaScript 动画(requestAnimationFrame)、关闭视差滚动、停止轮播和滑块、关闭 skeleton 效果并将微调器替换为静态指示器。
/* 基本动画(no-preference) */
.spinner {
width: 32px;
height: 32px;
border: 3px solid #e0e0e0;
border-top-color: #0066CC;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.skeleton {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e8e8e8 37%,
#f0f0f0 63%
);
background-size: 400% 100%;
animation: shimmer 1.4s ease infinite;
}
.parallax-bg {
transform: translateZ(-1px) scale(1.5);
transition: transform 0.3s ease-out;
}
/* Reduce Motion:关闭一切 */
@media (prefers-reduced-motion: reduce) {
.spinner {
animation: none;
border-top-color: #0066CC;
}
.skeleton {
animation: none;
background: #f0f0f0;
}
.parallax-bg {
transform: none;
transition: none;
}
.carousel {
scroll-behavior: auto;
overflow-x: auto;
}
.fade-in {
opacity: 1;
transform: none;
}
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}在 CSS 示例中,在 prefers-reduced-motion: reduce 时使用了全局动画重置:所有动画和过渡的 duration 为 0.01ms,iteration-count 为 1。单独重写了微调器(静态边框)、skeleton(灰色背景无闪烁)、视差(关闭)和轮播(scroll-behavior: auto 替代 smooth)。通用选择器 * — 所有元素的安全保障。
对于 JavaScript,可使用 matchMedia('(prefers-reduced-motion: reduce)').matches。如果为 true — 不要通过 requestAnimationFrame、setInterval 或 Web Animations API 运行动画。示例:带有 reduceMotion 的滑块不会自动旋转,只能通过按钮操作。GSAP、Framer Motion 和 Anime.js 库通过其无障碍选项支持此检查。
WCAG 2.2(2023)引入了新的标准 2.3.3 Animation from Interactions(AAA 级):如果动画由用户交互(点击、滚动、悬停)触发,它必须可以关闭或不超过 5 秒。与交互无关的动画(加载微调器、进度条)不在此标准范围内。对于 AA 级,动画不是传递信息的唯一方式即可。
标准 2.3.3 是在收到大量关于视差滚动和“下拉刷新“动画导致晕动症的投诉后添加的。W3C 建议所有网站和应用程序:动画元素不超过 5 秒,提供动画关闭开关(通过控制面板或 prefers-reduced-motion),不要使用脉冲和闪烁元素(对癫痫患者危险)。
为符合 WCAG 2.2,开发者应:检查 prefers-reduced-motion 并在设置激活时关闭动画,将动画最大持续时间设置为 5 秒(如果动画无法关闭),在应用设置中提供 UI 动画开关,不要将视差效果作为唯一的导航方式,并确保在启用 Reduce Motion 的情况下在所有浏览器和设备上进行测试。
要测试应用在 Reduce Motion 下的行为,请使用:iOS Simulator(在 Settings > Accessibility 中启用 Reduce Motion)、Android Emulator(启用“删除动画“)、Chrome DevTools(Rendering > Emulate CSS media feature prefers-reduced-motion)、Safari Web 检查器(在 macOS System Preferences > Accessibility > Display 中启用 Reduce Motion)。检查内容在无动画时是否仍然可访问和可读。
常见问题
Reduce Motion — 操作系统的无障碍功能,为前庭障碍患者关闭界面动画(视差、过渡、图标缩放)。在 Settings > Accessibility 中启用。系统通过 UIAccessibility(iOS)、Settings.Global(Android)或 prefers-reduced-motion(CSS)向应用发送信号,开发者可以调整动画。
在 iPhone 上,Reduce Motion 在 Settings > Accessibility > Motion > Reduce Motion 中启用。启用后,壁纸视差、iMessage 效果、应用打开动画和屏幕间过渡(替换为 cross-fade)将被关闭。UIKit 系统动画不会播放。设置将应用于所有应用,无需重启设备。
在 Android 中,动画可以通过两种方式关闭:设置 > 无障碍 > 删除动画(推荐给用户)或开发者选项 > 动画缩放 — 关闭。无障碍方法确保所有应用都能收到信号。开发者选项仅影响系统动画,在某些设备上可能被隐藏。
CSS 媒体功能 prefers-reduced-motion: reduce 允许在样式级别关闭动画。建议设置通用重置:animation-duration: 0.01ms、transition-duration: 0.01ms、animation-iteration-count: 1 应用于所有元素。单独重写特定组件(spinner、skeleton、视差)。JavaScript 通过 matchMedia('(prefers-reduced-motion: reduce)') 进行检查。
WCAG 2.2 标准 2.3.3(AAA)要求由用户交互触发的动画必须可以关闭或不超过 5 秒。AA 级要求动画不是传递信息的唯一方式即可。建议始终检查 prefers-reduced-motion 并在应用设置中提供 UI 动画开关。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。