- ใช้
clip-pathและmask-imageสำหรับการเปิดเผยรูปทรงเรขาคณิตที่กำหนดเองและการค่อยๆ เลือนหายไปอย่างราบรื่น - ใช้ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน (
animation-timeline: scroll()) สำหรับเอฟเฟกต์ที่เชื่อมโยงกับการเลื่อนซึ่งไม่จำเป็นแทน JS Listener - ใช้ View Transitions เพื่อสร้างภาพเคลื่อนไหวระหว่างสถานะเลย์เอาต์ที่ซับซ้อนได้อย่างราบรื่น
ประสิทธิภาพ
- แนะนำให้ใช้ภาพเคลื่อนไหว
opacityและtransform(รวมถึงพร็อพเพอร์ตี้การเปลี่ยนรูปแบบแต่ละรายการ เช่นtranslateแทนleft/right/top/bottom) เพื่อให้ภาพเคลื่อนไหวอยู่ในเทรด Compositor - ใช้
transition-behavior: allow-discrete,@starting-styleและ (สำหรับองค์ประกอบชั้นบนสุด เช่น<dialog>หรือ[popover])overlayเพื่อทำให้สถานะการเข้าและออกที่ไม่ต่อเนื่องเคลื่อนไหวโดยค่าเริ่มต้น โปรดดูทำให้การเข้าและออกขององค์ประกอบเคลื่อนไหว และทำให้องค์ประกอบเคลื่อนไหวเข้าและออกจากชั้นบนสุด
.popover-reveal {
/* Transition discrete display and top-layer overlay alongside opacity */
transition:
opacity ease-out,
display,
overlay;
transition-duration: 0.2s;
transition-behavior: allow-discrete;
}
การช่วยเหลือพิเศษ
ใช้ prefers-reduced-motion Media Query เพื่อปิดการเคลื่อนไหวที่หนักหน่วงสำหรับผู้ใช้ที่ต้องการ
- มีกลไกการหยุดชั่วคราว: อนุญาตให้ผู้ใช้หยุดภาพสไลด์ แบนเนอร์ หรือภาพเคลื่อนไหวอื่นๆ ที่ทำงานอัตโนมัติอย่างต่อเนื่อง
- ใช้มุมมองแบบคงที่เป็นค่าเริ่มต้น: ลองใช้สถานะแบบคงที่เป็นค่าเริ่มต้นและอนุญาตให้ผู้ใช้เลือกใช้การเคลื่อนไหว
- อย่าใช้แสงแฟลชเกินขีดจำกัด (3 ครั้งต่อวินาที): อย่าใช้แสงแฟลชที่สว่างและมืดสลับกันอย่างรวดเร็ว ซึ่งอาจทำให้เกิดอาการชักได้
อย่าใช้ animation-duration: 0.01ms; ทั่วทั้งระบบ เนื่องจากอาจทำให้ภาพเคลื่อนไหวบางอย่างดูไม่ราบรื่นยิ่งขึ้น
แต่ให้จัดการค่ากำหนดการเคลื่อนไหวเป็นกรณีๆ ไปโดยใช้หนึ่งใน 2 วิธี โดยฝังการค้นหา @media ไว้ในกฎเพื่อให้ทั้ง 2 เวอร์ชันอยู่ด้วยกัน ดังนี้
- เริ่มต้นด้วยการเปิดใช้ภาพเคลื่อนไหวโดยค่าเริ่มต้น และลดระดับลงโดยใช้
@media (prefers-reduced-motion: reduce):
/* Good: Dampen spin states or slow down animations for reduced motion */
.spinner {
animation: spin 1s linear infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
opacity: 0.5;
}
}
progress:not([value]) {
animation: slide 1s infinite linear;
@media (prefers-reduced-motion: reduce) {
animation-duration: 20s;
}
}
- เริ่มต้นโดยไม่มีภาพเคลื่อนไหวโดยค่าเริ่มต้น และเปิดใช้โดยใช้
@media (prefers-reduced-motion: no-preference):
/* Good: Default to static or gentle states and opt-in to full motion */
.spinner {
opacity: 0.5;
@media (prefers-reduced-motion: no-preference) {
opacity: 1;
animation: spin 1s linear infinite;
}
}
progress:not([value]) {
animation: slide 20s infinite linear;
@media (prefers-reduced-motion: no-preference) {
animation-duration: 1s;
}
}
การทำงานกับเหตุการณ์ภาพเคลื่อนไหวและการเปลี่ยนภาพใน JavaScript
ทั้งในอินเทอร์เฟซ AnimationEvent (animationstart, animationiteration, animationend, animationcancel) และ TransitionEvent (transitionrun, transitionstart, transitionend, transitioncancel) event.target จะให้องค์ประกอบ DOM ต้นทางแก่คุณ หากต้องการเข้าถึงออบเจ็กต์ Animation ที่เฉพาะเจาะจง (Animation, CSSAnimation หรือ CSSTransition) ที่ทริกเกอร์เหตุการณ์ ให้ใช้พร็อพเพอร์ตี้ event.animation แบบอ่านอย่างเดียว
เมื่อ event.animation ไม่พร้อมใช้งาน โดยปกติแล้วคุณจะค้นหาอินสแตนซ์ Animation ได้โดยเรียกใช้ event.target.getAnimations() และกรองตาม event.animationName (หรือ event.propertyName สำหรับการเปลี่ยน) อย่างไรก็ตาม เราขอแนะนำให้ใช้ event.animation โดยตรงเพื่อหลีกเลี่ยงสถานการณ์หลายอย่างที่การใช้ event.target.getAnimations() เป็นตัวกรองสำรองไม่ทำงานตามที่คาดไว้ ดังนี้
- ภาพเคลื่อนไหว Web Animations API (
element.animate()) ไม่มีanimationNameจึงไม่สามารถจับคู่ตามชื่อเมื่อกรองevent.target.getAnimations() - การกรองตาม
animationNameสำหรับAnimationEventจะไม่ชัดเจนเมื่อมีการแนบภาพเคลื่อนไหวหลายรายการที่มีชื่อเดียวกันกับองค์ประกอบเดียวกัน- โปรดทราบว่าการกรอง
transitionPropertyสำหรับTransitionEventจะทำงานได้เนื่องจากมีเพียง 1 เหตุการณ์ต่อพร็อพเพอร์ตี้การเปลี่ยนสถานะ
- โปรดทราบว่าการกรอง
event.targetจะชี้ไปยังองค์ประกอบ DOM ต้นทางเสมอ จึงไม่แยกความแตกต่างระหว่างภาพเคลื่อนไหวที่ทำงานในองค์ประกอบนั้นเองกับองค์ประกอบจำลองรายการใดรายการหนึ่ง ขณะที่event.animation.effect.pseudoElementจะแสดงองค์ประกอบจำลองเป้าหมายโดยตรง
// Works in most basic CSS animation cases, but fails for WAAPI animations,
// duplicate animation names on the same element, or pseudo-elements.
document.addEventListener('animationstart', (event) => {
const animation = event.target
.getAnimations()
.find((anim) => anim.animationName === event.animationName);
console.log(animation);
});
// PREFERRED: Directly access the exact Animation object via event.animation
document.addEventListener('animationstart', (event) => {
const animation = event.animation;
console.log(animation);
});
คุณสามารถตรวจหาการรองรับฟีเจอร์ได้โดยใช้ 'animation' in AnimationEvent.prototype (หรือ 'animation' in TransitionEvent.prototype)