Category: reference
CSS Animations & Transitions — ทำ UI ให้มีชีวิตชีวา
รวม pattern CSS animation และ transition ที่ใช้จริงในการสร้าง UI ที่ smooth — hover effects, micro-interactions, page transitions และ reduced motion
สารบัญ
Transition vs Animation
| Transition | Animation | |
|---|---|---|
| Trigger | ต้องมี state change (hover, :focus, class) | รันได้เอง หรือ trigger ด้วย class |
| Control | จาก A → B เท่านั้น | หลาย keyframes ได้ |
| Loop | ไม่ได้ | ได้ (infinite) |
| ใช้กับ | hover effects, state changes | loading, complex sequences |
Transition พื้นฐาน
.btn {
background: #2563eb;
transform: translateY(0);
/* ระบุ property ที่ transition + duration + easing */
transition: background 0.2s ease, transform 0.2s ease;
}
.btn:hover {
background: #1d4ed8;
transform: translateY(-2px);
}
Easing ที่ควรรู้
transition-timing-function: ease; /* slow-fast-slow (default) */
transition-timing-function: ease-in; /* slow start */
transition-timing-function: ease-out; /* slow end — เหมาะกับ enter animations */
transition-timing-function: ease-in-out; /* slow start + end */
transition-timing-function: linear; /* คงที่ตลอด — เหมาะกับ rotation */
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); /* spring bounce */
Card Hover Effect
.card {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
ใช้ transform และ opacity เท่านั้นสำหรับ animation ที่ smooth เพราะ GPU-accelerated ไม่กระตุ้น layout reflow
CSS Keyframe Animation
/* fade in จาก ล่าง */
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.section {
animation: fade-up 0.4s ease-out both;
}
animation-fill-mode: both ทำให้ element อยู่ใน state สุดท้ายหลัง animation จบ
Loading Spinner
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
width: 1.5rem;
height: 1.5rem;
border: 2px solid rgba(37, 99, 235, 0.2);
border-top-color: #2563eb;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
Skeleton Loading
@keyframes shimmer {
to { background-position: 200% center; }
}
.skeleton {
background: linear-gradient(
90deg,
rgba(15, 23, 42, 0.06) 25%,
rgba(15, 23, 42, 0.12) 50%,
rgba(15, 23, 42, 0.06) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s linear infinite;
border-radius: 6px;
}
Reduced Motion (Accessibility)
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
เสมอใส่ reduced motion ใน global CSS ผู้ใช้ที่มีปัญหา vestibular disorder อาจรู้สึกไม่สบายกับ animation
View Transitions API
สำหรับ page transitions ใน Astro:
<!-- src/layouts/Layout.astro -->
import ClientRouter from 'astro/components/ClientRouter.astro';
<ClientRouter />
/* กำหนด animation สำหรับ page transition */
::view-transition-old(root) {
animation: fade-out 0.2s ease-out;
}
::view-transition-new(root) {
animation: fade-in 0.2s ease-in;
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
}
Tips สำหรับ Performance
/* บอก browser ล่วงหน้าว่า element จะ animate */
.card {
will-change: transform;
}
/* ใช้หลัง animation จบ เพื่อปลด GPU memory */
.card:not(:hover) {
will-change: auto;
}
ใช้ will-change เฉพาะเมื่อจำเป็น และ apply บน element จำนวนน้อย — ใช้มากเกินไปทำให้ memory ขึ้น
/* ใช้ transform แทน top/left เสมอ */
/* ❌ */
.modal { top: 0; left: 0; transition: top 0.3s; }
/* ✅ */
.modal { transform: translateY(0); transition: transform 0.3s ease-out; }