React Native reduced motion: เคารพการตั้งค่าลดการเคลื่อนไหวตั้งแต่เฟรมแรก
แอนิเมชันที่ช่วยบอกว่าการ์ดเลื่อนไปไหน อาจทำให้ผู้ใช้บางคนเวียนหัวหรือเสียสมาธิครับ React Native เปิดค่าระบบผ่าน AccessibilityInfo.isReduceMotionEnabled() และ event reduceMotionChanged เพื่อให้แอปเปลี่ยน motion โดยเคารพการตั้งค่าของผู้ใช้
WHY: ลด motion ไม่ใช่ลบ feedback
ผู้ใช้ไม่ได้ขอให้หน้าจอหยุดสื่อสาร เขาขอให้หลีกเลี่ยงการเคลื่อนที่ที่กระตุ้นอาการ เช่น zoom ใหญ่, parallax หรือวัตถุวิ่งข้ามจอ เราจึงควรแทนด้วย fade สั้น, การเปลี่ยนสี หรือ state แบบทันที โดยยังบอกว่าการกดสำเร็จหรือหน้าเปลี่ยนแล้ว
เหมือนเปลี่ยนบันไดเลื่อนเป็นลิฟต์ครับ ปลายทางเดิม แต่ลดการเคลื่อนไหวที่ผู้โดยสารต้องรับรู้ระหว่างทาง
HOW: อ่านค่าเริ่มต้นและติดตามการเปลี่ยน
ตัวอย่างนี้เป็น fragment สำหรับแอป React Native ต้องตรวจต่อใน simulator/อุปกรณ์จริง เพราะ Scheduled Task ไม่มี mobile runtime
import { useEffect, useState } from "react";
import { AccessibilityInfo } from "react-native";
export function useReducedMotion(): boolean {
const [reduced, setReduced] = useState(false);
useEffect(() => {
let mounted = true;
AccessibilityInfo.isReduceMotionEnabled().then((value) => {
if (mounted) setReduced(value);
});
const subscription = AccessibilityInfo.addEventListener(
"reduceMotionChanged",
setReduced,
);
return () => {
mounted = false;
subscription.remove();
};
}, []);
return reduced;
}
ค่าเริ่ม false อาจทำให้เฟรมแรกเล่น motion ก่อน Promise ตอบ ถ้าหน้าจอมี transition ใหญ่ ให้เก็บสถานะ unknown | reduce | full แล้วรอค่าก่อนเริ่ม หรือให้ค่าเริ่มต้นเป็น motion ต่ำสำหรับเส้นทางแรก ความเร็วไม่ได้สำคัญกว่าการเคารพ preference
ออกแบบ fallback ให้ตรงสาเหตุ
| Motion ปกติ | เมื่อ reduce motion |
|---|---|
| slide เต็มหน้าจอ | cross-fade สั้นหรือเปลี่ยนทันที |
| parallax ตาม scroll | ตำแหน่งนิ่ง |
| scale เด้งหลายรอบ | เปลี่ยนสี/เส้นขอบหนึ่งครั้ง |
| progress spinner | progress text หรือแถบที่ไม่หมุนเมื่อทำได้ |
ทดสอบทั้งตอนเปิดค่าก่อนเข้าแอปและเปลี่ยนค่าขณะแอปรัน ตรวจ cleanup listener เมื่อ component unmount และอย่าซ่อนเนื้อหาจนกว่า animation จะจบ เพราะผู้ใช้ screen reader อาจเจอ state ที่ภาพกับ accessibility tree ไม่ตรงกัน
Recap
- อ่าน preference จากระบบ ไม่สร้าง toggle แยกโดยไม่จำเป็น
- ติดตาม
reduceMotionChangedและถอด listener ตอน unmount - ลดการเดินทาง/zoom แต่คง feedback ของสถานะ
- ทดสอบบนอุปกรณ์จริงทั้ง iOS และ Android