Reduce Motion — tính năng trợ năng của hệ điều hành và trình duyệt giúp tắt hoặc giảm chuyển động của giao diện để ngăn cảm giác khó chịu ở người dùng bị rối loạn tiền đình. Trên iOS, cài đặt này nằm ở Settings > Accessibility > Motion > Reduce Motion và tắt hiệu ứng chiều sâu, chuyển động chuyển màn hình và hiệu ứng biểu tượng. Trên Android, tùy chọn tương đương “Xóa hoạt ảnh” nằm ở Settings > Accessibility. Theo W3C WCAG 2.2, chuyển động quá mức có thể gây buồn nôn, chóng mặt và đau nửa đầu ở 15–35% người bị rối loạn tiền đình.
Điểm chính
Reduce Motion — cài đặt hệ thống yêu cầu hệ điều hành, trình duyệt và ứng dụng giảm thiểu hoặc tắt hoàn toàn chuyển động, di chuyển, phóng thu và hiệu ứng chiều sâu. Tính năng này dành cho người bị rối loạn tiền đình, ở những người này các yếu tố chuyển động trong giao diện gây buồn nôn, chóng mặt, mất phương hướng và đau nửa đầu.
Hệ tiền đình của tai trong chịu trách nhiệm về cảm giác thăng bằng và định hướng trong không gian. Khi mắt nhìn thấy chuyển động trên màn hình nhưng cơ quan tiền đình không cảm nhận được sự di chuyển tương ứng của cơ thể, sẽ xảy ra xung đột giác quan. Xung đột này kích hoạt phản ứng thực vật: buồn nôn, vã mồ hôi lạnh, chóng mặt. Theo Hiệp hội Rối loạn Tiền đình, khoảng 35% người trên 40 tuổi gặp cảm giác khó chịu do hiệu ứng chuyển động trong giao diện.
Điểm khác biệt cốt lõi giữa Reduce Motion và việc tắt chuyển động đơn giản là cấp độ hệ thống. Khi được bật, cài đặt được truyền đến mọi ứng dụng và trang web thông qua API của hệ điều hành hoặc hàm media CSS prefers-reduced-motion. Nhà phát triển không cần đoán người dùng có muốn chuyển động hay không — hệ điều hành sẽ thông báo. Đây là một phần của phương pháp progressive enhancement: chuyển động là sự cải tiến chứ không phải yếu tố bắt buộc.
Reduce Motion ảnh hưởng đến hiệu ứng chiều sâu (nền dịch chuyển khi nghiêng thiết bị), chuyển động chuyển màn hình (push, slide, fade), phóng thu biểu tượng khi chạm, hiệu ứng tin nhắn (bong bóng SMS), chuyển động tải (spinner, skeleton, chuyển động thanh tiến trình), hiệu ứng chiều sâu trong trò chơi và hiệu ứng cuộn parallax trên web. Hình ảnh tĩnh và video không bị ảnh hưởng.
| Loại chuyển động | iOS Reduce Motion | Android Remove Animation | CSS prefers-reduced-motion |
|---|---|---|---|
| Hiệu ứng chiều sâu hình nền | Tắt | Tắt | N/A |
| Chuyển màn hình | Thay bằng fade | Tắt | CSS transitions |
| Chuyển động biểu tượng | Tắt | Không ảnh hưởng | CSS animations |
| Spinner tải | Không ảnh hưởng | Tắt | CSS animations |
| Màn hình skeleton | Không ảnh hưởng | Thay bằng tĩnh | CSS animations |
Đối tượng chính của Reduce Motion là những người bị rối loạn tiền đình (bệnh Ménière, viêm mê đạo, BPPV), đau nửa đầu (đặc biệt có aura), say tàu xe và một số dạng động kinh. Tính năng này cũng hữu ích cho người lớn tuổi — những thay đổi do tuổi tác ở cơ quan tiền đình làm tăng độ nhạy với chuyển động — và cho người dùng bị ADHD, vì các yếu tố chuyển động làm mất tập trung khi đọc.
Trên iOS, Reduce Motion nằm ở Settings > Accessibility > Motion và gồm ba tùy chọn: Reduce Motion (công tắc chính), Auto-Play Message Effects (tự phát hiệu ứng iMessage) và Auto-Play Video Previews (tự phát video trong App Store). Reduce Motion mạnh nhất: nó tắt hiệu ứng chiều sâu hình nền, chuyển động mở ứng dụng (zoom), chuyển màn hình (push được thay bằng cross-fade) và hiệu ứng biểu tượng trên màn hình chính.
Về mặt kỹ thuật, iOS triển khai Reduce Motion ở cấp UIKit thông qua UIView animations và UIViewControllerAnimatedTransitioning. Khi bật cài đặt, UIView.animate có thời lượng > 0 được rút xuống còn 0,01 giây, và UIViewController.transitionDuration trả về 0. Với hiệu ứng chiều sâu, iOS tắt UIInterpolatingMotionEffect trên UIWindow. Nhà phát triển có thể kiểm tra trạng thái qua UIAccessibility.isReduceMotionEnabled.
Nhà phát triển có thể điều chỉnh chuyển động trên iOS theo Reduce Motion: thay các chuyển màn phức tạp bằng hiệu ứng fade, tắt hiệu ứng chiều sâu trong các thành phần UI tùy chỉnh và rút ngắn thời lượng chuyển động. SwiftUI cung cấp modifier đặc biệt .animation(nil) khi reduceMotion đang bật.
import UIKit
class MotionManager {
// Kiểm tra trạng thái Reduce Motion
static var isReduceMotionEnabled: Bool {
return UIAccessibility.isReduceMotionEnabled
}
// Chuyển động an toàn có xét đến 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 — bộ sửa đổi cho 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 }
}
}
// Đăng ký theo dõi thay đổi Reduce Motion
NotificationCenter.default.addObserver(
self,
selector: #selector(motionStatusChanged),
name: UIAccessibility.reduceMotionStatusDidChangeNotification,
object: nil
)Lớp MotionManager kiểm tra isReduceMotionEnabled và điều chỉnh thời lượng chuyển động: khi Reduce Motion bật, thời lượng được rút xuống còn 0,01 giây (thực chất là tức thì). Modifier .animation(nil) của SwiftUI tắt hoàn toàn chuyển động khi reduceMotion == true. Đăng ký theo dõi reduceMotionStatusDidChangeNotification cho phép chuyển đổi chuyển động một cách linh hoạt mà không cần khởi động lại ứng dụng.
Một tùy chọn riêng của iOS — Auto-Play Message Effects — kiểm soát việc phát hiệu ứng trong iMessage: chuyển động bóng bay, hoa giấy, tia laser và pháo hoa. Khi bật Reduce Motion, hiệu ứng iMessage tự động bị tắt. Nhà phát triển có thể kiểm tra tùy chọn này qua UIAccessibility.isReduceMotionEnabled — không có API riêng cho Message Effects; chúng gắn với cài đặt chung.
Trên Android, cài đặt “Xóa hoạt ảnh” nằm ở hai nơi: Settings > Accessibility > Xóa hoạt ảnh (cho cài đặt trợ năng) và Tùy chọn nhà phát triển > Tỷ lệ hoạt ảnh (cho nhà phát triển). Phiên bản trợ năng tắt mọi chuyển động của hệ thống, gồm cả chuyển màn hình, chuyển động mở ứng dụng và hiệu ứng tải. Tùy chọn nhà phát triển cho phép giảm tỷ lệ xuống 0.5x, 0.1x hoặc tắt hoàn toàn.
Về mặt kỹ thuật, Android triển khai việc tắt chuyển động qua các thuộc tính hệ thống: window_animation_scale, transition_animation_scale và animator_duration_scale. Với cài đặt trợ năng, cả ba đều nhận giá trị 0. Trình chạy hệ thống và các ứng dụng hệ thống kiểm tra các giá trị này. Với ứng dụng bên thứ ba, Android khuyến nghị dùng Jetpack Compose và hệ thống View, tự động đọc cài đặt hệ thống qua AnimationScaleObserver.
Nhà phát triển có thể kiểm tra trạng thái chuyển động trên Android qua Settings.Global và điều chỉnh giao diện. Jetpack Compose cung cấp AnimationScaleObserver để tự điều chỉnh. Giá trị then chốt là animator_duration_scale: nếu bằng 0, chuyển động bị tắt hoàn toàn.
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 {
// Kiểm tra việc tắt chuyển động
fun isAnimationDisabled(context: Context): Boolean {
return try {
Settings.Global.getFloat(
context.contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE
) == 0f
} catch (e: Settings.SettingNotFoundException) {
false
}
}
// Quan sát thay đổi chuyển động (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 — chuyển động có xét đến cài đặt
@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"
)
// Giao diện có chuyển động
}
Lớp AndroidMotionHelper kiểm tra ANIMATOR_DURATION_SCALE qua Settings.Global, cung cấp ContentObserver để theo dõi thay đổi và cho phép chuyển động Jetpack Compose thích ứng: khi chuyển động bị tắt, durationMillis = 0 (áp dụng tức thì). Với hệ thống View, hãy dùng ViewPropertyAnimator.withLayer() và kiểm tra isAnimationDisabled() trước khi bắt đầu một chuyển động.
Cài đặt trợ năng “Xóa hoạt ảnh” trên Android là cách duy nhất để người dùng thông thường tắt chuyển động. Tùy chọn nhà phát triển chỉ dành cho nhà phát triển và có thể bị ẩn. Cài đặt trợ năng được truyền đảm bảo đến mọi ứng dụng qua API hệ thống. Nhà phát triển nên dựa vào AccessibilityManager chứ không phải Tùy chọn nhà phát triển.
Hàm media CSS prefers-reduced-motion cho phép nhà phát triển web tắt hoặc thay chuyển động khi người dùng bật Reduce Motion trong hệ điều hành. Trình duyệt truyền giá trị reduce khi cài đặt hệ thống đang bật và no-preference nếu cài đặt chưa thay đổi. Được hỗ trợ bởi mọi trình duyệt hiện đại từ năm 2020: Chrome 74+, Safari 10.3+, Firefox 64+, Edge 79+.
Khuyến nghị của MDN và W3C — luôn đặt prefers-reduced-motion: reduce cùng với chuyển động. Người dùng bật Reduce Motion không nên thấy hiệu ứng nhấp nháy, hiệu ứng chiều sâu, băng chuyền tự phát và tải skeleton. Thay cho chuyển động, có thể hiển thị nội dung tĩnh hoặc dùng transition CSS với thời lượng tối thiểu. Dưới đây là ví dụ về sự thích ứng đầy đủ.
Với prefers-reduced-motion: reduce, trang web nên: tắt chuyển động CSS (@keyframes), thay transition CSS bằng thay đổi tức thì, dừng chuyển động JavaScript (requestAnimationFrame), tắt cuộn parallax, dừng băng chuyền và thanh trượt, tắt hiệu ứng skeleton và thay spinner bằng chỉ báo tĩnh.
/* Chuyển động cơ bản (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: tắt tất cả */
@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; }
}Trong ví dụ CSS, dùng cách đặt lại chuyển động toàn cục khi prefers-reduced-motion: reduce: mọi chuyển động và transition nhận thời lượng 0.01ms và iteration-count 1. Riêng spinner (viền tĩnh), skeleton (nền xám không nhấp nháy), parallax (đã tắt) và băng chuyền (scroll-behavior: auto thay vì smooth) được ghi đè. Bộ chọn vạn năng * là lớp bảo vệ cho mọi phần tử.
Với JavaScript, có sẵn matchMedia('(prefers-reduced-motion: reduce)').matches. Nếu là true — đừng chạy chuyển động qua requestAnimationFrame, setInterval hoặc Web Animations API. Ví dụ: thanh trượt khi reduceMotion không tự xoay, chỉ theo nút bấm. Các thư viện như GSAP, Framer Motion và Anime.js hỗ trợ kiểm tra này qua các tùy chọn trợ năng của chúng.
WCAG 2.2 (2023) đưa ra tiêu chí mới 2.3.3 Animation from Interactions (cấp AAA): nếu chuyển động được kích hoạt từ tương tác của người dùng (nhấp, cuộn, di chuột), nó phải có thể tắt được hoặc không vượt quá 5 giây. Chuyển động không liên quan đến tương tác (spinner tải, thanh tiến trình) không thuộc tiêu chí này. Với cấp AA, chỉ cần chuyển động không phải là cách duy nhất để truyền tải thông tin.
Tiêu chí 2.3.3 được bổ sung sau nhiều khiếu nại về say chuyển động do cuộn parallax và chuyển động “kéo để làm mới”. W3C khuyến nghị mọi trang web và ứng dụng: không chuyển động phần tử quá 5 giây, cung cấp công tắc tắt chuyển động (qua bảng điều khiển hoặc prefers-reduced-motion), không dùng phần tử nhấp nháy và chớp sáng (nguy hiểm với bệnh động kinh).
Để tuân thủ WCAG 2.2, nhà phát triển cần: kiểm tra prefers-reduced-motion và tắt chuyển động khi cài đặt đang bật, đặt thời lượng chuyển động tối đa 5 giây (nếu chuyển động không thể tắt), cung cấp công tắc UI cho chuyển động trong cài đặt ứng dụng, không dùng hiệu ứng chiều sâu làm cách điều hướng duy nhất và kiểm thử với Reduce Motion bật trên mọi trình duyệt và mọi thiết bị.
Để kiểm thử hành vi của ứng dụng với Reduce Motion, hãy dùng: iOS Simulator (bật Reduce Motion trong Settings > Accessibility), Android Emulator (bật “Xóa hoạt ảnh”), Chrome DevTools (Rendering > Emulate CSS media feature prefers-reduced-motion), trình kiểm tra web của Safari (bật Reduce Motion trong System Preferences của macOS > Accessibility > Display). Hãy đảm bảo nội dung vẫn truy cập được và đọc được khi không có chuyển động.
Câu hỏi thường gặp
Reduce Motion — tính năng trợ năng của hệ điều hành tắt chuyển động của giao diện (hiệu ứng chiều sâu, chuyển màn, phóng thu biểu tượng) cho người bị rối loạn tiền đình. Nó được bật trong Settings > Accessibility. Hệ thống truyền tín hiệu đến ứng dụng qua UIAccessibility (iOS), Settings.Global (Android) hoặc prefers-reduced-motion (CSS), và nhà phát triển có thể điều chỉnh chuyển động.
Trên iPhone, Reduce Motion được bật trong Settings > Accessibility > Motion > Reduce Motion. Sau khi bật, hiệu ứng chiều sâu hình nền, hiệu ứng iMessage, chuyển động mở ứng dụng và chuyển màn hình bị tắt (được thay bằng cross-fade). Chuyển động hệ thống UIKit không được phát. Cài đặt áp dụng cho mọi ứng dụng mà không cần khởi động lại thiết bị.
Trên Android, chuyển động bị tắt theo hai cách: Settings > Accessibility > Xóa hoạt ảnh (khuyến nghị cho người dùng) hoặc Tùy chọn nhà phát triển > Tỷ lệ hoạt ảnh — tắt nó đi. Cách trợ năng đảm bảo mọi ứng dụng nhận được tín hiệu. Tùy chọn nhà phát triển chỉ ảnh hưởng đến chuyển động hệ thống và có thể bị ẩn trên một số thiết bị.
Hàm media CSS prefers-reduced-motion: reduce cho phép tắt chuyển động ở cấp kiểu dáng. Nên đặt bộ đặt lại vạn năng: animation-duration: 0.01ms, transition-duration: 0.01ms, animation-iteration-count: 1 cho mọi phần tử. Riêng ghi đè các thành phần cụ thể (spinner, skeleton, parallax). JavaScript được kiểm tra qua matchMedia('(prefers-reduced-motion: reduce)').
WCAG 2.2 tiêu chí 2.3.3 (AAA) yêu cầu chuyển động do tương tác của người dùng phải tắt được hoặc không vượt quá 5 giây. Với AA, chỉ cần chuyển động không phải là cách duy nhất để truyền tải thông tin. Luôn nên kiểm tra prefers-reduced-motion và cung cấp công tắc UI cho chuyển động trong cài đặt ứng dụng.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm