CSS
Transitions & Animations
animation properties
@keyframes กำหนด 'เนื้อหา' ของ animation — animation properties กำหนด 'วิธีเล่น' เช่น เล่นนานแค่ไหน เล่นซ้ำกี่รอบ หน่วงเวลาก่อนเริ่ม และค้างที่ค่าสุดท้ายหรือไม่
หัวข้อนี้คืออะไร
animation properties คือกลุ่ม CSS property ที่ควบคุมวิธีที่ browser เล่น @keyframes animation เปรียบเทียบให้เห็นชัด: @keyframes = บทเพลง (กำหนดว่าเพลงมีเนื้อหาอะไร) animation properties = เครื่องเล่นเพลง (กำหนดว่าจะเล่นเร็วแค่ไหน เล่นซ้ำกี่รอบ เริ่มเมื่อไหร่) ถ้ามีแค่ @keyframes แต่ไม่มี animation properties — animation จะไม่เกิดขึ้นเลย animation properties มี 7 ตัวหลัก:
- animation-name — ชื่อ @keyframes ที่จะนำมาเล่น ต้องตรงกันทุกตัวอักษร
- animation-duration — ระยะเวลาของ 1 รอบ animation เช่น 0.5s, 1.2s
- animation-timing-function — รูปแบบความเร็ว เช่น ease, linear, ease-in-out
- animation-delay — หน่วงเวลาก่อนเริ่ม animation เช่น 0.3s
- animation-iteration-count — จำนวนรอบที่เล่น เช่น 1, 3, infinite
- animation-direction — ทิศทางการเล่น เช่น normal, reverse, alternate
- animation-fill-mode — สถานะของ element ก่อน/หลัง animation เล่น เช่น forwards, backwards, both
ทำไมหัวข้อนี้สำคัญ
@keyframes บอกแค่ว่า animation มีอะไรบ้าง แต่ไม่ได้บอกว่าจะเล่นอย่างไร animation properties คือตัวที่ตัดสินใจว่า: เล่นนานแค่ไหน — animation-duration กำหนดว่า 1 รอบใช้เวลาเท่าไหร่ เริ่มเมื่อไหร่ — animation-delay ทำให้ animation ของหลาย element ไม่เริ่มพร้อมกัน สร้าง stagger effect เล่นซ้ำหรือเปล่า — animation-iteration-count: infinite ทำให้ loader หมุนไม่หยุด ค้างที่ค่าสุดท้ายไหม — animation-fill-mode: forwards ทำให้ fade-in ไม่กลับเป็นโปร่งใสหลัง animation จบ ถ้าตั้งค่าผิด เช่น ลืม duration หรือ fill-mode — animation อาจดูผิดปกติทันทีที่จบ ข้อควรระวัง: animation ที่มากเกินไปในหน้าเดียว หรือ infinite loop ที่ไม่จำเป็น ทำให้ UI ดูวุ่นวายและรบกวนการอ่าน
ตัวอย่างจากชีวิตจริง
ลองนึกถึงภาพยนตร์ในโรงหนัง: บท (script) = @keyframes — กำหนดว่าในแต่ละฉากมีอะไรเกิดขึ้น ผู้กำกับ = animation properties — ตัดสินใจว่าแต่ละฉากเล่นช้าหรือเร็ว ต้องเล่นซ้ำกี่รอบ เริ่มเมื่อไหร่ ตัวอย่าง animation-delay ในชีวิตจริง: เมนูที่ item แต่ละตัว slide เข้ามาทีละอัน ห่างกัน 0.05s — ใช้ animation-delay ที่ต่างกันสำหรับแต่ละ item ตัวอย่าง iteration-count ในชีวิตจริง: Loading spinner หมุนไม่หยุด — animation-iteration-count: infinite Badge แจ้งเตือนเด้ง 3 ครั้ง — animation-iteration-count: 3 ตัวอย่าง fill-mode ในชีวิตจริง: Notification toast ที่ slide เข้ามาแล้วค้างไว้ — animation-fill-mode: forwards ป้องกันไม่ให้กลับออกไปทันที
แนวคิดหลักที่ต้องเข้าใจ
ลองดูผลของ animation property แต่ละตัวแบบ interactive:
| Property | ค่าตัวอย่าง | ความหมาย |
|---|---|---|
| animation-name | fadeIn, spin, slideUp | ชื่อ @keyframes ที่จะใช้ |
| animation-duration | 0.5s, 1s, 300ms | ระยะเวลา 1 รอบ animation |
| animation-timing-function | ease, linear, ease-in-out | รูปแบบความเร็ว |
| animation-delay | 0s, 0.3s, 500ms | หน่วงเวลาก่อนเริ่ม |
| animation-iteration-count | 1, 3, infinite | จำนวนรอบที่เล่น |
| animation-direction | normal, reverse, alternate, alternate-reverse | ทิศทางการเล่น |
| animation-fill-mode | none, forwards, backwards, both | สถานะก่อน/หลัง animation |
การทำงานทีละขั้นตอน
วิธีตั้งค่า animation อย่างถูกต้องตั้งแต่ต้น:
- 1. ประกาศ @keyframes ก่อนเสมอ — กำหนดชื่อที่สื่อความหมาย เช่น fadeIn, slideUp, pulse
- 2. ระบุ animation-name ให้ตรงกับชื่อ @keyframes — ตัวใหญ่-เล็กมีผล fadeIn ≠ fadein
- 3. ระบุ animation-duration เสมอ — ค่าเริ่มต้น 0s ทำให้ animation จบทันทีก่อนเห็น
- 4. ตั้ง animation-fill-mode: forwards ถ้าต้องการให้ element ค้างที่ค่าสุดท้าย — มิฉะนั้นจะกลับค่าเดิมทันทีที่ animation จบ
- 5. ใช้ animation-delay เพื่อสร้าง stagger — เช่น delay 0.05s ต่อ item ทำให้ดูเข้ามาทีละตัว
- 6. ตั้ง animation-iteration-count: infinite เฉพาะเมื่อจำเป็น — เช่น loader หรือ pulse เท่านั้น
ตัวอย่างเชิงเทคนิค — shorthand และ longhand
animation มี shorthand ที่รวมทุก property ในบรรทัดเดียว — ลองแก้โค้ดแล้ว reload เพื่อดูผล:
shorthand animation ลำดับ: name → duration → timing → delay → iteration → direction → fill-mode
/* Longhand — แยกทีละ property */
.element {
animation-name: fadeIn;
animation-duration: 0.5s;
animation-timing-function: ease;
animation-delay: 0.2s;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: forwards;
}
/* Shorthand — รวมในบรรทัดเดียว */
/* ลำดับ: name duration timing delay iteration direction fill-mode */
.element {
animation: fadeIn 0.5s ease 0.2s 1 normal forwards;
}
/* ย่อสั้นที่สุดที่ใช้บ่อย (ละ default ออก) */
.element {
animation: fadeIn 0.5s ease forwards;
}
/* หลาย animation พร้อมกัน */
.element {
animation: fadeIn 0.5s ease forwards,
pulse 2s ease 0.5s infinite;
}จุดที่ผู้เริ่มต้นมักสับสน
- ลืม animation-duration — ค่าเริ่มต้นคือ 0s animation เล่นจบทันทีก่อนที่ตาจะเห็น ต้องระบุ duration ทุกครั้ง
- animation-fill-mode ไม่ถูกต้อง — ถ้า fade-in element แล้ว element กลับหายไปทันทีหลัง animation จบ แสดงว่าขาด fill-mode: forwards
- animation-delay ทำให้เห็นค่าเริ่มต้นผิด — ถ้า fade-in จาก opacity: 0 และมี delay element จะแสดงเป็น opacity: 1 ก่อนแล้วค่อยหาย แก้ด้วย animation-fill-mode: both
- infinite loop ที่ไม่ควรมี — loader ควร infinite แต่ entrance animation ไม่ควร ถ้าใส่ infinite ผิดที่ element จะ animate ไม่หยุด
- เขียน animation shorthand ลำดับผิด — duration และ delay เป็นค่าเวลาทั้งคู่ ค่าแรกคือ duration ค่าที่สองคือ delay เช่น animation: spin 1s 0.3s infinite หมายถึง duration=1s delay=0.3s
เปรียบเทียบ animation-fill-mode แต่ละแบบ
fill-mode กำหนดว่า element อยู่ในสถานะไหนก่อนเริ่มและหลัง animation จบ:
| fill-mode | ก่อน animation (มี delay) | หลัง animation จบ |
|---|---|---|
| none (default) | สถานะปกติ | กลับสถานะปกติ |
| forwards | สถานะปกติ | ค้างที่ค่า 100% |
| backwards | ค่าจาก 0% keyframe | กลับสถานะปกติ |
| both | ค่าจาก 0% keyframe | ค้างที่ค่า 100% |
สรุปท้ายบทแบบจำง่าย
@keyframes = "เนื้อหา animation" animation properties = "วิธีเล่น animation" property ที่ต้องจำ 3 อันดับแรก:
- animation-name — ต้องตรงกับชื่อ @keyframes ทุกตัวอักษร
- animation-duration — ต้องระบุเสมอ ค่าเริ่มต้น 0s = animation ไม่เกิด
- animation-fill-mode: forwards — ใส่เมื่อต้องการให้ element ค้างที่ค่าสุดท้าย ไม่กลับค่าเดิม
- shorthand ที่ใช้บ่อยที่สุด: animation: name duration timing forwards
Lab 1 — ผูก keyframes กับ element
โจทย์: @keyframes popIn ถูกประกาศไว้แล้ว แต่ .badge ยังไม่มี animation เพิ่ม animation ให้ .badge: - animation-name: popIn - animation-duration: 0.4s - animation-timing-function: ease-out - animation-fill-mode: forwards
Lab 2 — stagger ด้วย animation-delay
โจทย์: มี .item 3 ชิ้น แต่ทุกชิ้น animate พร้อมกันพร้อมกันหมด ทำให้แต่ละชิ้น slide เข้ามาทีละอัน โดย: - .item-1 delay: 0s - .item-2 delay: 0.15s - .item-3 delay: 0.3s และเพิ่ม animation-fill-mode: both ให้ทุก item (เพื่อซ่อนก่อน animation เริ่ม)
Lab 3 — แก้ animation ที่ตั้งค่าผิด
โจทย์: .notification มี animation ที่มีปัญหา 2 จุด: 1. animation-fill-mode: backwards ทำให้ notification หายไปหลัง animation จบ (ควรใช้ forwards) 2. animation-iteration-count: 3 ทำให้ slide เข้ามา 3 รอบซ้ำๆ (entrance animation ควรเล่นแค่ครั้งเดียว) แก้ให้ notification slide เข้ามาครั้งเดียวและค้างอยู่