Reduce Motion — ฟีเจอร์การเข้าถึงของระบบปฏิบัติการและเบราว์เซอร์ที่ปิดหรือลดแอนิเมชันของอินเทอร์เฟซเพื่อป้องกันความไม่สบายตัวของผู้ใช้ที่มีความผิดปกติของระบบทรงตัว ใน iOS การตั้งค่านี้อยู่ใน Settings > Accessibility > Motion > Reduce Motion และปิดเอฟเฟกต์พารัลแลกซ์ แอนิเมชันการเปลี่ยนหน้าจอ และเอฟเฟกต์ไอคอน ใน Android ตัวเลือกที่เทียบเท่า “ลบแอนิเมชัน” อยู่ใน Settings > Accessibility ตามข้อมูลของ W3C WCAG 2.2 แอนิเมชันที่มากเกินไปอาจทำให้คลื่นไส้ เวียนศีรษะ และไมเกรนใน 15–35% ของผู้ที่มีความผิดปกติของระบบทรงตัว
ประเด็นสำคัญ
Reduce Motion — การตั้งค่าระบบที่สั่งให้ระบบปฏิบัติการ เบราว์เซอร์ และแอปลดหรือปิดแอนิเมชัน การเคลื่อนไหว การย่อขยาย และเอฟเฟกต์พารัลแลกซ์โดยสิ้นเชิง ฟีเจอร์นี้ออกแบบมาสำหรับผู้ที่มีความผิดปกติของระบบทรงตัว ซึ่งองค์ประกอบที่เคลื่อนไหวในอินเทอร์เฟซทำให้เกิดอาการคลื่นไส้ เวียนศีรษะ สับสน และไมเกรน
ระบบทรงตัวในหูชั้นในรับผิดชอบการทรงตัวและการรับรู้ทิศทางในอวกาศ เมื่อดวงตาเห็นการเคลื่อนไหวบนหน้าจอ แต่ระบบทรงตัวไม่รับรู้การเคลื่อนไหวของร่างกายที่สอดคล้องกัน ก็เกิดความขัดแย้งทางประสาทสัมผัส ความขัดแย้งนี้กระตุ้นปฏิกิริยาอัตโนมัติ: คลื่นไส้ เหงื่อออกเย็น เวียนศีรษะ ตามข้อมูลของ Vestibular Disorders Association ผู้ที่มีอายุมากกว่า 40 ปีประมาณ 35% รู้สึกไม่สบายจากเอฟเฟกต์การเคลื่อนไหวในอินเทอร์เฟซ
ความแตกต่างสำคัญระหว่าง Reduce Motion กับการปิดแอนิเมชันธรรมดาคือระดับระบบ เมื่อเปิดใช้งาน การตั้งค่านี้จะถูกส่งไปยังทุกแอปและเว็บไซต์ผ่าน API ของระบบปฏิบัติการหรือฟังก์ชันมีเดีย CSS prefers-reduced-motion นักพัฒนาไม่ต้องเดาว่าผู้ใช้ต้องการแอนิเมชันหรือไม่ — ระบบปฏิบัติการแจ้งให้ทราบ นี่เป็นส่วนหนึ่งของแนวทาง progressive enhancement: แอนิเมชันคือการปรับปรุง ไม่ใช่องค์ประกอบที่จำเป็น
Reduce Motion ส่งผลต่อเอฟเฟกต์พารัลแลกซ์ (พื้นหลังเคลื่อนเมื่อเอียงอุปกรณ์) แอนิเมชันการเปลี่ยนหน้าจอ (push, slide, fade) การย่อขยายไอคอนเมื่อแตะ เอฟเฟกต์ข้อความ (บอลลูน SMS) แอนิเมชันการโหลด (spinner, skeleton, แอนิเมชันแถบความคืบหน้า) พารัลแลกซ์ในเกม และเอฟเฟกต์พารัลแลกซ์สโครลบนเว็บ รูปภาพนิ่งและวิดีโอไม่ได้รับผลกระทบ
| ประเภทแอนิเมชัน | iOS Reduce Motion | Android Remove Animation | CSS prefers-reduced-motion |
|---|---|---|---|
| พารัลแลกซ์วอลเปเปอร์ | ปิด | ปิด | N/A |
| การเปลี่ยนหน้าจอ | แทนที่ด้วยเฟด | ปิด | CSS transitions |
| แอนิเมชันไอคอน | ปิด | ไม่มีผล | CSS animations |
| Spinner การโหลด | ไม่มีผล | ปิด | CSS animations |
| หน้าจอ Skeleton | ไม่มีผล | แทนที่ด้วยภาพนิ่ง | CSS animations |
กลุ่มเป้าหมายหลักของ Reduce Motion คือผู้ที่มีความผิดปกติของระบบทรงตัว (โรคเมนิแยร์ โรคเขาวงกตอักเสบ BPPV) ไมเกรน (โดยเฉพาะแบบมีออร่า) อาการเมาเรือ และโรคโรคลมชักบางรูปแบบ ฟีเจอร์นี้ยังมีประโยชน์สำหรับผู้ใช้สูงอายุ — การเปลี่ยนแปลงของระบบทรงตัวตามอายุเพิ่มความไวต่อการเคลื่อนไหว — และสำหรับผู้ใช้ที่เป็นโรคสมาธิสั้น เนื่องจากองค์ประกอบที่เคลื่อนไหวรบกวนการอ่าน
บน 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 ใช้ Reduce Motion ในระดับ UIKit ผ่าน UIView animations และ UIViewControllerAnimatedTransitioning เมื่อเปิดการตั้งค่า UIView.animate ที่มีระยะเวลา > 0 จะถูกลดเหลือ 0.01 วินาที และ UIViewController.transitionDuration คืนค่า 0 สำหรับพารัลแลกซ์ iOS ปิด UIInterpolatingMotionEffect บน UIWindow นักพัฒนาสามารถตรวจสอบสถานะผ่าน UIAccessibility.isReduceMotionEnabled
นักพัฒนาสามารถปรับแอนิเมชันบน iOS ให้เข้ากับ Reduce Motion ได้: แทนที่การเปลี่ยนหน้าที่ยุ่งยากด้วยเอฟเฟกต์เฟด ปิดพารัลแลกซ์ในคอมโพเนนต์ UI ที่กำหนดเอง และลดระยะเวลาแอนิเมชัน SwiftUI มีตัวดัดแปลงพิเศษ .animation(nil) เมื่อเปิดใช้ reduceMotion
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 วินาที (แทบจะทันที) ตัวดัดแปลง .animation(nil) ของ SwiftUI ปิดแอนิเมชันโดยสิ้นเชิงเมื่อ reduceMotion == true การสมัครรับเหตุการณ์ reduceMotionStatusDidChangeNotification ช่วยให้สลับแอนิเมชันแบบไดนามิกได้โดยไม่ต้องรีสตาร์ทแอป
ตัวเลือกแยกของ iOS — Auto-Play Message Effects — ควบคุมการเล่นเอฟเฟกต์ใน iMessage: แอนิเมชันลูกโป่ง กระดาษโปรย เลเซอร์ และดอกไม้ไฟ เมื่อเปิด Reduce Motion เอฟเฟกต์ iMessage จะถูกปิดโดยอัตโนมัติ นักพัฒนาสามารถตรวจสอบตัวเลือกนี้ผ่าน UIAccessibility.isReduceMotionEnabled — ไม่มี API แยกสำหรับ Message Effects; พวกมันผูกกับการตั้งค่าทั่วไป
บน Android การตั้งค่า “ลบแอนิเมชัน” อยู่ในสองที่: Settings > Accessibility > ลบแอนิเมชัน (สำหรับการตั้งค่าการเข้าถึง) และตัวเลือกนักพัฒนา > มาตราส่วนแอนิเมชัน (สำหรับนักพัฒนา) เวอร์ชันการเข้าถึงปิดแอนิเมชันระบบทั้งหมด รวมถึงการเปลี่ยนหน้าจอ แอนิเมชันการเปิดแอป และเอฟเฟกต์การโหลด ตัวเลือกนักพัฒนาช่วยลดมาตราส่วนเป็น 0.5x, 0.1x หรือปิดโดยสิ้นเชิง
ในทางเทคนิค Android ใช้การปิดแอนิเมชันผ่านพร็อพเพอร์ตี้ของระบบ: window_animation_scale, transition_animation_scale และ animator_duration_scale เมื่อใช้การตั้งค่าการเข้าถึง ทั้งสามจะได้รับค่า 0 ตัวเรียกใช้ระบบและแอประบบตรวจสอบค่าเหล่านี้ สำหรับแอปของบริษัทอื่น Android แนะนำให้ใช้ Jetpack Compose และระบบ View ซึ่งอ่านการตั้งค่าระบบโดยอัตโนมัติผ่าน AnimationScaleObserver
นักพัฒนาสามารถตรวจสอบสถานะแอนิเมชันบน Android ผ่าน Settings.Global และปรับ 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 ตรวจสอบ ANIMATOR_DURATION_SCALE ผ่าน Settings.Global มี ContentObserver สำหรับติดตามการเปลี่ยนแปลง และช่วยให้แอนิเมชัน Jetpack Compose ปรับตัวได้: เมื่อปิดแอนิเมชัน durationMillis = 0 (นำไปใช้ทันที) สำหรับระบบ View ใช้ ViewPropertyAnimator.withLayer() และตรวจสอบ isAnimationDisabled() ก่อนเริ่มแอนิเมชัน
การตั้งค่าการเข้าถึง “ลบแอนิเมชัน” บน Android เป็นวิธีเดียวที่ผู้ใช้ทั่วไปจะปิดแอนิเมชันได้ ตัวเลือกนักพัฒนามีไว้สำหรับนักพัฒนาเท่านั้นและอาจถูกซ่อนไว้ การตั้งค่าการเข้าถึงจะถูกส่งไปยังทุกแอปอย่างแน่นอนผ่าน API ของระบบ นักพัฒนาควรพึ่งพา AccessibilityManager แทนตัวเลือกนักพัฒนา
ฟังก์ชันมีเดีย CSS prefers-reduced-motion ช่วยให้นักพัฒนาเว็บปิดหรือแทนที่แอนิเมชันเมื่อผู้ใช้เปิด 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 และแทนที่ spinner ด้วยตัวบ่งชี้ภาพนิ่ง
/* แอนิเมชันพื้นฐาน (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: แอนิเมชันและการเปลี่ยนทั้งหมดได้รับระยะเวลา 0.01ms และ iteration-count 1 แยกกันมีการแทนที่ spinner (เส้นขอบนิ่ง) 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 วินาที แอนิเมชันที่ไม่เกี่ยวกับการโต้ตอบ (spinner โหลด, แถบความคืบหน้า) ไม่อยู่ในเกณฑ์นี้ สำหรับระดับ AA ก็เพียงพอที่แอนิเมชันไม่ใช่วิธีเดียวในการถ่ายทอดข้อมูล
เกณฑ์ 2.3.3 ถูกเพิ่มหลังจากข้อร้องเรียนมากมายเกี่ยวกับอาการเมาเครื่องจากการสโครลพารัลแลกซ์และแอนิเมชัน “ดึงเพื่อรีเฟรช” W3C แนะนำเว็บไซต์และแอปทั้งหมด: อย่าเคลื่อนไหวองค์ประกอบเกิน 5 วินาที ให้สวิตช์ปิดแอนิเมชัน (ผ่านแผงควบคุมหรือ prefers-reduced-motion) และอย่าใช้องค์ประกอบที่เต้นเป็นจังหวะและกะพริบ (อันตรายต่อผู้ป่วยโรคลมชัก)
เพื่อให้เป็นไปตาม WCAG 2.2 นักพัฒนาต้อง: ตรวจสอบ prefers-reduced-motion และปิดแอนิเมชันเมื่อการตั้งค่าเปิดอยู่ กำหนดระยะเวลาแอนิเมชันสูงสุด 5 วินาที (หากปิดแอนิเมชันไม่ได้) ให้สวิตช์ UI สำหรับแอนิเมชันในการตั้งค่าแอป อย่าใช้เอฟเฟกต์พารัลแลกซ์เป็นวิธีนำทางวิธีเดียว และทดสอบโดยเปิด Reduce Motion ในทุกเบราว์เซอร์และทุกอุปกรณ์
สำหรับการทดสอบพฤติกรรมของแอปกับ Reduce Motion ให้ใช้: iOS Simulator (เปิด Reduce Motion ใน Settings > Accessibility) Android Emulator (เปิด “ลบแอนิเมชัน”) Chrome DevTools (Rendering > Emulate CSS media feature prefers-reduced-motion) และเว็บอินสเปกเตอร์ของ Safari (เปิด Reduce Motion ใน System Preferences ของ macOS > Accessibility > Display) ตรวจสอบว่าเนื้อหายังเข้าถึงได้และอ่านได้โดยไม่ต้องใช้แอนิเมชัน
คำถามที่พบบ่อย
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 แอนิเมชันปิดได้สองวิธี: Settings > Accessibility > ลบแอนิเมชัน (แนะนำสำหรับผู้ใช้) หรือตัวเลือกนักพัฒนา > มาตราส่วนแอนิเมชัน — ปิดมัน วิธีการเข้าถึงรับประกันว่าแอปทั้งหมดได้รับสัญญาณ ตัวเลือกนักพัฒนาส่งผลต่อแอนิเมชันระบบเท่านั้นและอาจถูกซ่อนในบางอุปกรณ์
ฟังก์ชันมีเดีย 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 สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม