Article · programming
HTML & CSS EP13: CSS Transitions, Transforms และ Animations — สร้างเว็บไซต์ที่มีชีวิตชีวา
HTML & CSS
บทเรียน HTML และ CSS ตอนที่ 13 เรียนรู้การสร้างเอฟเฟกต์และภาพเคลื่อนไหวบนเว็บไซต์ด้วย CSS Transitions, Transforms, Keyframe Animations, Hover States, Focus States และ Reduced Motion พร้อมแนวทางเพิ่มประสิทธิภาพและตัวอย่างหน้าเว็บ Interactive Product Showcase แบบ HTML เต็มหน้าที่สามารถทดลองได้ทันที
HTML & CSS EP13: CSS Transitions, Transforms และ Animations
จากบทเรียนก่อนหน้าที่เราเรียนรู้การจัดวางเว็บไซต์ด้วย CSS Grid, Flexbox และ Responsive Design ตอนนี้เราจะเพิ่มอีกหนึ่งองค์ประกอบสำคัญให้เว็บไซต์ นั่นคือการตอบสนองต่อการกระทำของผู้ใช้และการเคลื่อนไหวที่ช่วยสื่อสารข้อมูลได้ชัดเจนขึ้น ไม่ว่าจะเป็นปุ่มที่เปลี่ยนสีอย่างนุ่มนวล การ์ดสินค้าที่ขยับเล็กน้อยเมื่อเลื่อนเมาส์เหนือ หรือแถบแจ้งเตือนที่ค่อย ๆ ปรากฏขึ้น
CSS สามารถสร้างการเคลื่อนไหวได้โดยไม่จำเป็นต้องเขียน JavaScript ทุกครั้ง เราจะใช้ `transition` สำหรับการเปลี่ยนค่าระหว่างสถานะ ใช้ `transform` เพื่อเปลี่ยนตำแหน่ง หมุน หรือปรับขนาดองค์ประกอบ และใช้ `@keyframes` เพื่อกำหนดลำดับการเคลื่อนไหวหลายช่วงอย่างเป็นระบบ
💡 สิ่งที่คุณจะทำได้หลังเรียนจบสร้างปุ่มที่ตอบสนองอย่างนุ่มนวล การ์ดสินค้าที่มี Hover Effect แอนิเมชันเปิดเผยเนื้อหา การเคลื่อนไหวแบบวนซ้ำ และหน้าแสดงสินค้าแบบ Interactive โดยคำนึงถึงประสิทธิภาพ การใช้งานด้วยคีย์บอร์ด และผู้ใช้ที่ต้องการลดการเคลื่อนไหว
1. เข้าใจความแตกต่างระหว่าง Transition, Transform และ Animation
ทั้งสามแนวคิดทำงานร่วมกันได้ แต่มีหน้าที่แตกต่างกัน `transition` กำหนดวิธีเปลี่ยนจากค่าหนึ่งไปสู่อีกค่าหนึ่งในช่วงเวลาที่กำหนด `transform` ใช้เปลี่ยนรูปทรงหรือตำแหน่งที่แสดงผลขององค์ประกอบ และ `animation` ใช้เล่นลำดับการเปลี่ยนแปลงที่กำหนดด้วย `@keyframes` ซึ่งสามารถทำงานโดยไม่ต้องมีการ Hover หรือคลิกก็ได้
| คุณสมบัติ | หน้าที่ | ตัวอย่างการใช้งาน |
|---|---|---|
| transition | เปลี่ยนค่าระหว่างสถานะอย่างต่อเนื่อง | ปุ่มค่อย ๆ เปลี่ยนสีเมื่อ Hover |
| transform | เปลี่ยนตำแหน่ง หมุน ย่อ หรือขยาย | การ์ดเลื่อนขึ้นเล็กน้อย |
| @keyframes | กำหนดลำดับการเปลี่ยนแปลงหลายช่วง | จุดแจ้งเตือนเต้นเป็นจังหวะ |
| animation | กำหนดการเล่น Keyframes ระยะเวลา และจำนวนรอบ | แสดงเอฟเฟกต์ปรากฏขึ้นเมื่อโหลดหน้า |
2. เริ่มต้นด้วย CSS Transition
Transition เหมาะกับการเปลี่ยนสถานะที่มีจุดเริ่มต้นและจุดสิ้นสุดชัดเจน เช่น สีพื้นหลัง ความทึบ เงา หรือการเลื่อนตำแหน่ง เมื่อผู้ใช้เลื่อนเมาส์ออกจากปุ่ม เบราว์เซอร์สามารถเปลี่ยนกลับสู่สถานะเดิมได้โดยอัตโนมัติ หากกำหนด Transition ไว้ในสถานะพื้นฐาน
<button class="button" type="button">ทดลองเลื่อนเมาส์</button>
<style>
.button {
padding: 12px 20px;
border: 0;
border-radius: 10px;
background: #2563eb;
color: white;
font: inherit;
cursor: pointer;
transition: background-color 200ms ease,
transform 200ms ease,
box-shadow 200ms ease;
}
.button:hover {
background: #1d4ed8;
transform: translateY(-2px);
box-shadow: 0 8px 18px rgb(37 99 235 / 20%);
}
</style>
ในตัวอย่างนี้ `transition` มีการระบุทั้งคุณสมบัติ ระยะเวลา และ Timing Function โดยใช้ `ease` เพื่อให้การเปลี่ยนแปลงเริ่มและจบอย่างนุ่มนวล การระบุเฉพาะคุณสมบัติที่ต้องการเปลี่ยนยังช่วยให้โค้ดชัดเจนกว่าการใช้ `transition: all` ซึ่งอาจทำให้คุณสมบัติอื่นเปลี่ยนตามโดยไม่ตั้งใจ
2.1 โครงสร้างของ Transition
รูปแบบทั่วไปคือ `transition: property duration timing-function delay;` โดย `property` คือคุณสมบัติที่ต้องการเปลี่ยน `duration` คือระยะเวลาของการเปลี่ยน `timing-function` คือรูปแบบความเร็วระหว่างการเปลี่ยน และ `delay` คือเวลารอก่อนเริ่ม ตัวอย่างเช่น `transition: opacity 250ms ease-in-out 100ms;` จะเปลี่ยนความทึบภายใน 250 มิลลิวินาทีหลังจากรอ 100 มิลลิวินาที
| Timing Function | ลักษณะการเคลื่อนไหว | เหมาะกับ |
|---|---|---|
| linear | ความเร็วคงที่ | การเคลื่อนไหวที่ต้องการอัตราสม่ำเสมอ |
| ease | เริ่มและจบอย่างนุ่มนวล | เอฟเฟกต์ทั่วไปบน UI |
| ease-in | เริ่มช้าแล้วเร็วขึ้น | องค์ประกอบที่เริ่มเคลื่อนออก |
| ease-out | เริ่มเร็วแล้วช้าลง | องค์ประกอบที่กำลังเข้าสู่ตำแหน่ง |
| ease-in-out | เริ่มช้า เร็วขึ้น แล้วช้าลง | การเปลี่ยนสถานะที่ต้องการความนุ่มนวลทั้งสองด้าน |
3. ใช้ Transform เพื่อเคลื่อนย้าย หมุน และปรับขนาด
คุณสมบัติ `transform` ใช้ปรับลักษณะการแสดงผลขององค์ประกอบ โดยไม่ต้องเปลี่ยนตำแหน่งปกติขององค์ประกอบอื่นในเอกสารเหมือนการแก้ Layout โดยตรง จึงเหมาะกับเอฟเฟกต์ที่ต้องการให้การ์ดขยับหรือปุ่มขยายเล็กน้อยโดยไม่ดันเนื้อหาข้างเคียง
.move-card:hover {
transform: translateY(-6px);
}
.zoom-image:hover {
transform: scale(1.04);
}
.rotate-icon:hover {
transform: rotate(12deg);
}
.shift-and-scale:hover {
transform: translateY(-4px) scale(1.02);
}
.flip-icon {
transform: rotate(180deg);
}
`translate()` ใช้เลื่อนตำแหน่ง `scale()` ใช้ย่อหรือขยาย `rotate()` ใช้หมุน และ `skew()` ใช้เอียงองค์ประกอบได้ หากใช้หลายฟังก์ชันในค่าเดียวกัน ควรตรวจสอบลำดับของฟังก์ชัน เพราะลำดับการแปลงอาจทำให้ผลลัพธ์ต่างกัน ควรใช้การขยายอย่างพอเหมาะเพื่อไม่ให้การ์ดชนกับองค์ประกอบข้างเคียง
⚠️ Transform ไม่ได้เปลี่ยนพื้นที่ Layout เดิมเมื่อใช้ `transform: scale()` องค์ประกอบอาจดูใหญ่ขึ้น แต่พื้นที่ที่สงวนไว้ใน Layout ยังคงเป็นขนาดเดิม จึงอาจทับซ้อนกับสิ่งรอบข้างได้ ควรเผื่อพื้นที่และทดสอบการแสดงผลบนหน้าจอหลายขนาด
4. สร้าง Hover Effects ให้การ์ดสินค้า
การ์ดสินค้ามักใช้เอฟเฟกต์เพื่อช่วยสื่อว่ารายการนั้นสามารถเปิดดูรายละเอียดได้ เราสามารถเปลี่ยนเงา ยกการ์ดขึ้นเล็กน้อย หรือขยายภาพภายในกรอบ โดยไม่จำเป็นต้องใช้ JavaScript สำหรับการเปลี่ยนสถานะเมื่อเมาส์อยู่เหนือองค์ประกอบ
.product-card {
overflow: hidden;
border: 1px solid #e2e8f0;
border-radius: 16px;
background: white;
box-shadow: 0 4px 12px rgb(15 23 42 / 5%);
transition: transform 220ms ease,
box-shadow 220ms ease,
border-color 220ms ease;
}
.product-card:hover {
transform: translateY(-5px);
border-color: #93c5fd;
box-shadow: 0 16px 32px rgb(15 23 42 / 12%);
}
.product-card__image {
display: block;
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
transition: transform 300ms ease;
}
.product-card:hover .product-card__image {
transform: scale(1.04);
}
การใช้ `overflow: hidden` กับกรอบภาพช่วยตัดส่วนภาพที่ขยายเกินขอบ แต่ต้องระวังไม่ใช้กับคอนเทนเนอร์ที่มีเมนูหรือ Focus Outline ซึ่งจำเป็นต้องแสดงออกมานอกกรอบ ในตัวอย่างจริงควรแยกกรอบภาพออกจากเนื้อหาการ์ดเพื่อควบคุมการตัดภาพได้ตรงจุด
5. ใช้ Keyframes เพื่อกำหนดลำดับการเคลื่อนไหว
เมื่อการเคลื่อนไหวมีหลายช่วงหรือควรเล่นเองโดยไม่ต้องรอ Hover ให้ใช้ `@keyframes` โดยกำหนดสถานะเริ่มต้นด้วย `from` และสถานะสุดท้ายด้วย `to` หรือระบุเป็นเปอร์เซ็นต์ เช่น `0%`, `50%` และ `100%` เพื่อควบคุมลำดับการเปลี่ยนแปลงได้ละเอียดขึ้น
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.intro-panel {
animation: fade-in-up 500ms ease-out both;
}
คำสั่ง `animation` ในตัวอย่างระบุชื่อ Keyframes ระยะเวลา และ Timing Function ส่วน `both` ใน `animation-fill-mode` ทำให้เบราว์เซอร์ใช้ค่าจาก Keyframes ทั้งในช่วงรอเริ่มและหลังจบตามที่กำหนด ควรตรวจสอบค่าเริ่มต้นขององค์ประกอบด้วย เพื่อไม่ให้เกิดการกระพริบก่อนแอนิเมชันเริ่มทำงาน
5.1 ควบคุมจำนวนรอบและทิศทาง
คุณสามารถใช้ `animation-iteration-count` เพื่อกำหนดจำนวนรอบ หรือใช้ค่า `infinite` เพื่อวนซ้ำอย่างต่อเนื่อง และใช้ `animation-direction` เพื่อกำหนดทิศทางการเล่น เช่น `alternate` ทำให้รอบถัดไปเล่นย้อนทิศทาง เหมาะกับเอฟเฟกต์ขยับขึ้นลงเล็กน้อย แต่ไม่ควรใช้การเคลื่อนไหววนซ้ำกับองค์ประกอบจำนวนมากโดยไม่มีเหตุผล
@keyframes gentle-pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
.status-dot {
animation: gentle-pulse 1.8s ease-in-out infinite;
}
.float-icon {
animation: float-up-down 2.5s ease-in-out infinite alternate;
}
@keyframes float-up-down {
from { transform: translateY(0); }
to { transform: translateY(-8px); }
}
⚠️ ใช้แอนิเมชันวนซ้ำอย่างมีเหตุผลการเคลื่อนไหวต่อเนื่องอาจรบกวนสมาธิ ทำให้หน้าเว็บดูวุ่นวาย หรือสร้างความไม่สบายให้ผู้ใช้บางคน ควรใช้กับข้อมูลที่มีความหมาย เช่น สถานะกำลังทำงาน และเปิดโอกาสให้ลดหรือหยุดการเคลื่อนไหวเมื่อไม่จำเป็น
6. สร้างลำดับแอนิเมชันด้วย Delay
หากต้องการให้รายการหลายชิ้นปรากฏต่อกัน สามารถใช้ `animation-delay` เพื่อหน่วงเวลาแต่ละรายการ วิธีนี้ช่วยนำสายตาไปตามลำดับของเนื้อหาได้ แต่ควรจำกัดช่วงเวลารวมให้สั้นและหลีกเลี่ยงการทำให้ผู้ใช้ต้องรอนานก่อนเข้าถึงข้อมูลหลัก
.feature-item {
animation: fade-in-up 450ms ease-out both;
}
.feature-item:nth-child(2) {
animation-delay: 100ms;
}
.feature-item:nth-child(3) {
animation-delay: 200ms;
}
.feature-item:nth-child(4) {
animation-delay: 300ms;
}
วิธีนี้เรียกว่า Staggered Animation โดยแต่ละรายการเริ่มในเวลาที่ต่างกันเล็กน้อย เหมาะกับกลุ่มการ์ดหรือรายการคุณสมบัติ แต่ถ้ารายการมีจำนวนมาก ควรใช้เอฟเฟกต์อย่างระมัดระวังและพิจารณาว่าจำเป็นต้องทำให้ทุกรายการเคลื่อนไหวหรือไม่
7. ใช้ Hover, Focus และ Active อย่างครอบคลุม
เว็บไซต์ที่ใช้ Hover Effect ควรออกแบบสถานะสำหรับผู้ใช้คีย์บอร์ดด้วย เพราะผู้ใช้บางคนไม่ใช้เมาส์ การกำหนด `:focus-visible` ช่วยแสดงตัวบ่งชี้โฟกัสเมื่อต้องใช้งานผ่านคีย์บอร์ด ส่วน `:active` ใช้แสดงสถานะขณะกดปุ่ม การเปลี่ยนสถานะเหล่านี้ควรช่วยให้เข้าใจการตอบสนองโดยไม่ทำให้การใช้งานสับสน
.action-button {
transition: background-color 180ms ease,
transform 180ms ease;
}
.action-button:hover {
background-color: #1d4ed8;
}
.action-button:focus-visible {
outline: 3px solid #93c5fd;
outline-offset: 4px;
}
.action-button:active {
transform: translateY(1px);
}
@media (hover: none) {
.action-button:hover {
background-color: #2563eb;
}
}
Media Feature `hover: none` ช่วยตรวจสอบว่าอุปกรณ์หลักไม่มีความสามารถ Hover แบบแม่นยำ แต่ไม่ควรใช้เพื่อลบข้อมูลหรือฟังก์ชันที่ผู้ใช้จำเป็นต้องเข้าถึง ให้แน่ใจว่าข้อมูลสำคัญไม่ถูกแสดงเฉพาะเมื่อเลื่อนเมาส์เหนือองค์ประกอบเท่านั้น
8. เคารพการตั้งค่าลดการเคลื่อนไหวของผู้ใช้
ระบบปฏิบัติการและอุปกรณ์บางประเภทเปิดให้ผู้ใช้ตั้งค่าลดการเคลื่อนไหวเพื่อลดความไม่สบายจากแอนิเมชัน เว็บไซต์สามารถตรวจสอบการตั้งค่านี้ด้วย Media Query `prefers-reduced-motion` แล้วลดหรือปิดแอนิเมชันที่ไม่จำเป็น โดยยังคงรักษาข้อมูลและความหมายขององค์ประกอบไว้
@media (prefers-reduced-motion: reduce) {
.decorative-animation,
.status-dot,
.float-icon {
animation: none;
}
.product-card,
.product-card__image,
.action-button {
transition-duration: 0.01ms;
}
html {
scroll-behavior: auto;
}
}
✅ หลักการสำคัญด้าน Accessibilityการลดการเคลื่อนไหวไม่ควรทำให้เนื้อหาหายไป หากองค์ประกอบใช้ Animation เพื่อเปิดเผยข้อมูล ต้องออกแบบให้ข้อมูลยังมองเห็นหรือเข้าถึงได้เมื่อปิด Animation ด้วย
9. เลือกคุณสมบัติที่เหมาะสมเพื่อประสิทธิภาพ
การเคลื่อนไหวบางชนิดอาจทำให้เบราว์เซอร์ต้องคำนวณ Layout หรือวาดภาพใหม่บ่อยครั้ง โดยเฉพาะเมื่อเปลี่ยนคุณสมบัติที่กระทบขนาดและตำแหน่งขององค์ประกอบจำนวนมาก สำหรับเอฟเฟกต์ทั่วไป ควรพิจารณาใช้ `transform` และ `opacity` ก่อน เพราะมักเหมาะกับการทำภาพเคลื่อนไหวที่มีประสิทธิภาพ อย่างไรก็ตาม ประสิทธิภาพจริงขึ้นอยู่กับหน้าเว็บ อุปกรณ์ และเบราว์เซอร์ จึงควรทดสอบกับงานจริง
| คุณสมบัติ | การใช้งาน | แนวทาง |
|---|---|---|
| transform | เลื่อน หมุน หรือขยายองค์ประกอบ | เหมาะกับเอฟเฟกต์การเคลื่อนไหวหลายประเภท |
| opacity | ค่อย ๆ ปรากฏหรือจางหาย | ใช้ร่วมกับ transform ได้ |
| width และ height | ปรับขนาด Layout | อาจทำให้ต้องคำนวณ Layout ใหม่ |
| top และ left | เปลี่ยนตำแหน่งตาม Layout | พิจารณาใช้ transform เมื่อเป็นการเคลื่อนไหวเพื่อการแสดงผล |
| box-shadow | เปลี่ยนความลึกขององค์ประกอบ | ใช้พอเหมาะและทดสอบบนอุปกรณ์เป้าหมาย |
อย่าเพิ่ม `will-change` ให้ทุกองค์ประกอบโดยอัตโนมัติ เพราะอาจใช้ทรัพยากรหน่วยความจำมากเกินความจำเป็น ควรใช้เมื่อมีหลักฐานจากการวัดประสิทธิภาพว่าจำเป็นจริง และถอดออกเมื่อไม่ต้องการแล้ว
10. สร้าง Interactive Product Showcase แบบเต็มหน้า
ตัวอย่างต่อไปนี้เป็นหน้า HTML แบบเต็มสำหรับทดลองในเบราว์เซอร์ มีส่วนหัว การ์ดสินค้า เอฟเฟกต์ Hover แอนิเมชันปรากฏตัว และปุ่มที่เปลี่ยนสถานะข้อความเมื่อคลิก โค้ดใช้ HTML, CSS และ JavaScript ขนาดเล็กเฉพาะส่วนที่ต้องตอบสนองต่อการคลิก ส่วนการเคลื่อนไหวหลักใช้ CSS
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="ตัวอย่างหน้าแสดงสินค้าที่ใช้ CSS Transitions, Transforms และ Animations">
<title>Interactive Product Showcase</title>
<style>
* { box-sizing: border-box; }
:root {
--brand: #4f46e5;
--brand-dark: #3730a3;
--ink: #172033;
--muted: #64748b;
--surface: #ffffff;
--page: #f5f7ff;
--border: #e2e8f0;
}
body {
margin: 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: var(--ink);
background: var(--page);
line-height: 1.6;
}
.container {
width: min(100% - 32px, 1100px);
margin-inline: auto;
}
.header {
background: var(--surface);
border-bottom: 1px solid var(--border);
}
.nav {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
padding-block: 18px;
}
.brand {
margin: 0;
font-size: 1.25rem;
font-weight: 850;
letter-spacing: -.03em;
}
.brand span { color: var(--brand); }
.nav a {
color: var(--muted);
text-decoration: none;
}
.nav a:hover { color: var(--brand); }
.hero {
padding-block: clamp(48px, 8vw, 84px) 40px;
text-align: center;
animation: fade-up 550ms ease-out both;
}
.eyebrow {
display: inline-block;
padding: 6px 12px;
border-radius: 999px;
background: #e0e7ff;
color: var(--brand-dark);
font-size: .85rem;
font-weight: 750;
}
h1 {
max-width: 760px;
margin: 18px auto 12px;
font-size: clamp(2rem, 1.2rem + 3vw, 3.5rem);
line-height: 1.15;
letter-spacing: -.04em;
}
.hero p {
max-width: 660px;
margin: 0 auto;
color: var(--muted);
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
gap: 20px;
padding-bottom: 48px;
}
.product-card {
display: flex;
flex-direction: column;
min-width: 0;
padding: 22px;
border: 1px solid var(--border);
border-radius: 20px;
background: var(--surface);
box-shadow: 0 6px 20px rgb(15 23 42 / 4%);
animation: fade-up 550ms ease-out both;
transition: transform 220ms ease, box-shadow 220ms ease, border-color 220ms ease;
}
.product-card:nth-child(2) { animation-delay: 100ms; }
.product-card:nth-child(3) { animation-delay: 200ms; }
.product-card:hover {
transform: translateY(-6px);
border-color: #a5b4fc;
box-shadow: 0 18px 36px rgb(79 70 229 / 12%);
}
.product-art {
display: grid;
place-items: center;
min-height: 150px;
margin-bottom: 18px;
overflow: hidden;
border-radius: 14px;
background: linear-gradient(135deg, #e0e7ff, #f5d0fe);
font-size: 3.5rem;
}
.product-card:nth-child(2) .product-art { background: linear-gradient(135deg, #d1fae5, #dbeafe); }
.product-card:nth-child(3) .product-art { background: linear-gradient(135deg, #fef3c7, #fce7f3); }
.product-art span {
display: inline-block;
transition: transform 280ms ease;
}
.product-card:hover .product-art span { transform: scale(1.15) rotate(-4deg); }
.product-card h2 { margin: 0; font-size: 1.25rem; }
.product-card p { color: var(--muted); margin: 10px 0 18px; }
.price { margin-top: auto; margin-bottom: 16px; font-size: 1.4rem; font-weight: 850; }
.button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 44px;
width: 100%;
padding: 10px 16px;
border: 0;
border-radius: 11px;
background: var(--brand);
color: white;
font: inherit;
font-weight: 750;
cursor: pointer;
transition: background-color 180ms ease, transform 180ms ease;
}
.button:hover { background: var(--brand-dark); }
.button:active { transform: translateY(1px); }
.button:focus-visible {
outline: 3px solid #a5b4fc;
outline-offset: 3px;
}
.status {
min-height: 28px;
margin: 0 0 36px;
color: var(--brand-dark);
text-align: center;
font-weight: 700;
}
.footer {
padding-block: 24px;
border-top: 1px solid var(--border);
color: var(--muted);
text-align: center;
font-size: .9rem;
}
@keyframes fade-up {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 520px) {
.nav { align-items: flex-start; flex-direction: column; }
.hero { text-align: left; }
h1 { margin-inline: 0; }
.hero p { margin-inline: 0; }
.product-grid { gap: 14px; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
transition-duration: .01ms !important;
}
}
</style>
</head>
<body>
<header class="header">
<div class="container nav">
<p class="brand">Level<span>Lance</span></p>
<a href="#products">เลือกดูสินค้า ↓</a>
</div>
</header>
<main>
<section class="container hero">
<span class="eyebrow">Interactive UI Demo</span>
<h1>เลือกเครื่องมือที่ช่วยให้ธุรกิจของคุณเติบโต</h1>
<p>ตัวอย่างหน้าแสดงสินค้าที่ใช้ CSS สร้างการตอบสนองอย่างนุ่มนวล พร้อมรองรับหน้าจอขนาดเล็กและการใช้งานด้วยคีย์บอร์ด</p>
</section>
<section class="container product-grid" id="products" aria-label="รายการสินค้า">
<article class="product-card">
<div class="product-art" aria-hidden="true"><span>🛍️</span></div>
<h2>ระบบร้านค้าออนไลน์</h2>
<p>จัดการรายการสินค้าและนำเสนอข้อมูลให้ลูกค้าค้นหาได้สะดวก</p>
<div class="price">เริ่มต้น ฿490</div>
<button class="button" type="button" data-product="ระบบร้านค้าออนไลน์">สนใจรายการนี้</button>
</article>
<article class="product-card">
<div class="product-art" aria-hidden="true"><span>📊</span></div>
<h2>ระบบจัดการธุรกิจ</h2>
<p>รวมข้อมูลสำคัญและช่วยให้ทีมติดตามการทำงานได้เป็นระบบ</p>
<div class="price">เริ่มต้น ฿690</div>
<button class="button" type="button" data-product="ระบบจัดการธุรกิจ">สนใจรายการนี้</button>
</article>
<article class="product-card">
<div class="product-art" aria-hidden="true"><span>💬</span></div>
<h2>ระบบดูแลลูกค้า</h2>
<p>จัดระเบียบการติดต่อและช่วยให้ทีมตอบคำถามได้รวดเร็วขึ้น</p>
<div class="price">เริ่มต้น ฿390</div>
<button class="button" type="button" data-product="ระบบดูแลลูกค้า">สนใจรายการนี้</button>
</article>
</section>
<p class="container status" id="status-message" role="status" aria-live="polite">เลือกสินค้าเพื่อทดลองการตอบสนองของปุ่ม</p>
</main>
<footer class="footer">
<div class="container">ตัวอย่างเพื่อการเรียนรู้ HTML, CSS Transitions, Transforms และ Animations</div>
</footer>
<script>
const statusMessage = document.querySelector('#status-message');
const productButtons = document.querySelectorAll('[data-product]');
productButtons.forEach((button) => {
button.addEventListener('click', () => {
const productName = button.dataset.product;
statusMessage.textContent = `คุณเลือก: ${productName} — นี่เป็นตัวอย่างการตอบสนองของหน้าเว็บ`;
});
});
</script>
</body>
</html>
11. แก้ปัญหาแอนิเมชันที่ทำงานไม่ตรงตามต้องการ
หาก Transition ไม่ทำงาน ให้ตรวจสอบว่าคุณสมบัติที่เปลี่ยนสามารถเปลี่ยนผ่านอย่างต่อเนื่องได้หรือไม่ และมีการเปลี่ยนค่าระหว่างสถานะจริงหรือไม่ หาก Animation ไม่ปรากฏ ให้ตรวจสอบชื่อ Keyframes การสะกดชื่อคุณสมบัติ ระยะเวลา และกฎ CSS ที่อาจเขียนทับค่าเดิม รวมถึงตรวจสอบการตั้งค่า Reduced Motion ของอุปกรณ์
| อาการ | สาเหตุที่อาจเกิดขึ้น | วิธีตรวจสอบ |
|---|---|---|
| Hover แล้วไม่เปลี่ยน | Selector ไม่ตรงหรือถูกกฎอื่นเขียนทับ | ตรวจสอบชื่อคลาสและลำดับ CSS |
| การ์ดกระโดดทันที | ไม่มี Transition ในสถานะพื้นฐาน | กำหนด Transition ที่องค์ประกอบหลัก |
| แอนิเมชันไม่เริ่ม | ชื่อ Keyframes ผิดหรือ Animation ถูกปิด | ตรวจสอบชื่อและกฎ Media Query |
| องค์ประกอบทับกัน | Transform ขยายเกินพื้นที่เดิม | ลด Scale หรือเพิ่มพื้นที่รอบองค์ประกอบ |
| หน้าเว็บทำงานช้า | มีเอฟเฟกต์จำนวนมากหรือเปลี่ยนคุณสมบัติที่มีต้นทุนสูง | ลดจำนวนแอนิเมชันและตรวจวัดประสิทธิภาพ |
12. แบบฝึกหัดท้ายบท
สร้างการ์ดสินค้าอย่างน้อยสามใบด้วย HTML และ CSS กำหนด Transition ให้การ์ดขยับขึ้นเล็กน้อยเมื่อ Hover เพิ่ม Transform ให้ไอคอนหรือภาพประกอบ และสร้าง Keyframes สำหรับแสดงการ์ดอย่างนุ่มนวล จากนั้นเพิ่ม Focus State สำหรับคีย์บอร์ดและ Media Query สำหรับ `prefers-reduced-motion` ทดลองลบกฎแต่ละส่วนแล้วสังเกตความแตกต่าง
แบบฝึกหัดเพิ่มเติม: สร้างปุ่มที่มีสถานะชัดเจน
สร้างปุ่มที่มีสถานะปกติ Hover, Focus และ Active โดยใช้สีพื้นหลัง เส้นขอบ และ Transform ที่แตกต่างกัน ตรวจสอบให้แน่ใจว่าข้อความยังอ่านง่าย และผู้ใช้ที่ไม่ใช้เมาส์สามารถเห็นได้ว่าปุ่มใดกำลังได้รับ Focus
แบบฝึกหัดเพิ่มเติม: สร้าง Animation แบบวนซ้ำ
สร้างจุดสถานะที่ขยายและย่ออย่างนุ่มนวลด้วย @keyframes กำหนดระยะเวลาประมาณ 1.5–2 วินาที และทำให้หยุดหรือถูกลดการเคลื่อนไหวเมื่อผู้ใช้เปิดการตั้งค่า Reduce Motion หลีกเลี่ยงการใช้เอฟเฟกต์กะพริบถี่ ๆ
13. Checklist ก่อนนำไปใช้จริง
ก่อนเผยแพร่เว็บไซต์ ให้ตรวจสอบว่าแอนิเมชันมีวัตถุประสงค์ชัดเจน ไม่ขัดขวางการอ่าน ไม่ทำให้ปุ่มเคลื่อนจนกดยาก และไม่ทำให้เนื้อหาสำคัญหายไป ผู้ใช้คีย์บอร์ดต้องเข้าถึงปุ่มและลิงก์ได้ ขณะเดียวกันอุปกรณ์ที่ตั้งค่า Reduce Motion ต้องยังใช้งานหน้าเว็บได้ครบถ้วน
✅ Checklist สำหรับนักพัฒนาใช้ Transition กับการเปลี่ยนสถานะที่เหมาะสม เลือก Transform สำหรับการเคลื่อนไหวที่ไม่จำเป็นต้องเปลี่ยน Layout จำกัดแอนิเมชันวนซ้ำ ระบุ Focus Indicator ทดสอบบนมือถือ และรองรับ prefers-reduced-motion
14. สรุปบทเรียน EP13
ในบทนี้คุณได้เรียนรู้วิธีใช้ CSS Transitions เพื่อเปลี่ยนสถานะอย่างนุ่มนวล ใช้ Transforms เพื่อเลื่อน หมุน และปรับขนาดองค์ประกอบ ใช้ Keyframes เพื่อสร้างแอนิเมชันหลายช่วง และใช้ Hover, Focus และ Active เพื่อให้หน้าเว็บตอบสนองต่อการใช้งานหลายรูปแบบ คุณยังได้เรียนรู้แนวทางลดต้นทุนการวาดภาพและเคารพการตั้งค่าลดการเคลื่อนไหวของผู้ใช้
เมื่อนำเทคนิคเหล่านี้ไปใช้ร่วมกับ Responsive Design จาก EP12 คุณจะสามารถสร้างหน้าเว็บที่ไม่เพียงจัดวางได้เหมาะสมกับทุกขนาดหน้าจอ แต่ยังมีการตอบสนองที่ชัดเจน ใช้งานง่าย และดูเป็นมืออาชีพ โดยควรจำไว้เสมอว่าแอนิเมชันมีหน้าที่ช่วยสื่อสาร ไม่ใช่ดึงความสนใจจนรบกวนเนื้อหาหลัก
การเคลื่อนไหวที่ดีไม่ใช่การทำให้ทุกอย่างขยับ แต่คือการช่วยให้ผู้ใช้เข้าใจว่าระบบกำลังตอบสนองต่อสิ่งที่พวกเขาทำ
Reach within 500px of the page end and stay for at least 5 minutes to earn EXP once per day.
⏳ Reading timer is running...