Category: guide
CSS Scroll-Driven Animations
Animate elements ตาม scroll position โดยใช้ animation-timeline: scroll() และ view() — ไม่ต้องการ JavaScript หรือ ScrollTrigger
สารบัญ
Scroll-Driven Animations คืออะไร
แทนที่จะให้ JS คอย listen scroll events และปรับ style เอง CSS Scroll-Driven Animations ผูก animation progress เข้ากับ scroll position โดยตรง — ไม่มี JavaScript, ไม่มี requestAnimationFrame, ไม่ jank
scroll() — Progress Bar
/* Classic scroll progress bar ที่ด้านบนหน้า */
@keyframes grow-x {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.progress-bar {
position: fixed;
top: 0; left: 0;
width: 100%; height: 3px;
background: #2563eb;
transform-origin: left;
animation: grow-x linear;
animation-timeline: scroll(); /* ✓ ผูกกับ scroll ของ document */
animation-fill-mode: both;
}
scroll() Parameters
/* scroll(scroller axis) */
animation-timeline: scroll(); /* root document, block axis */
animation-timeline: scroll(root); /* explicit: root document */
animation-timeline: scroll(nearest); /* nearest scrolling ancestor */
animation-timeline: scroll(self); /* element นั้นเอง (ถ้า overflow: scroll) */
animation-timeline: scroll(block); /* vertical scroll (default) */
animation-timeline: scroll(inline); /* horizontal scroll */
animation-timeline: scroll(y); /* vertical (same as block for LTR) */
animation-timeline: scroll(x); /* horizontal */
/* Combined */
animation-timeline: scroll(nearest inline);
view() — Animate เมื่อ Element เข้า Viewport
/* Fade-in เมื่อ element scroll เข้ามาใน viewport */
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-on-scroll {
animation: fade-in-up 0.6s ease-out both;
animation-timeline: view();
/* เริ่ม animate เมื่อ element เริ่มเข้า viewport,
จบเมื่อ element ผ่าน 30% ของ viewport */
animation-range: entry 0% entry 30%;
}
animation-range
/* Ranges ที่ใช้ได้ */
animation-range: cover; /* ตลอดที่ element อยู่ใน scroll container */
animation-range: contain; /* ตั้งแต่ element เข้าจนถึงออกทั้งหมด */
animation-range: entry; /* ขณะที่ element กำลังเข้า viewport */
animation-range: exit; /* ขณะที่ element กำลังออก viewport */
animation-range: entry-crossing;
animation-range: exit-crossing;
/* กำหนด start และ end แยก */
animation-range-start: entry 0%;
animation-range-end: entry 50%;
/* หรือ shorthand */
animation-range: entry 0% entry 50%;
Named Timeline — Scroll Container แยก
/* เมื่อต้องการ animate element จาก scroll ของ container อื่น */
.scroll-container {
overflow-y: scroll;
height: 300px;
scroll-timeline-name: --my-scroll; /* ✓ ตั้งชื่อ */
scroll-timeline-axis: block;
}
.animated-element {
animation: slide-in linear;
animation-timeline: --my-scroll; /* ✓ อ้างอิงชื่อ */
}
Stagger Effect
/* animate หลาย elements พร้อมกัน แต่ delay ต่างกัน */
@keyframes slide-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.card {
animation: slide-up ease-out both;
animation-timeline: view();
animation-range: entry 0% entry 40%;
}
.card:nth-child(1) { animation-delay: 0ms; }
.card:nth-child(2) { animation-delay: 100ms; }
.card:nth-child(3) { animation-delay: 200ms; }
.card:nth-child(4) { animation-delay: 300ms; }
Parallax Effect
/* Image เลื่อนช้ากว่า scroll */
@keyframes parallax {
from { transform: translateY(-20%); }
to { transform: translateY(20%); }
}
.hero-image {
animation: parallax linear;
animation-timeline: view();
animation-range: cover; /* ตลอดที่ element อยู่ใน view */
}
เปรียบกับ GSAP ScrollTrigger
/* CSS Scroll-Driven: ง่ายกว่า ไม่ต้องการ JS */
.element {
animation: fade-in ease-out both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
/* GSAP ScrollTrigger: ยืดหยุ่นกว่า, รองรับ browser เก่า */
gsap.from('.element', {
opacity: 0, y: 30,
scrollTrigger: {
trigger: '.element',
start: 'top 80%',
end: 'top 50%',
scrub: true,
},
});
ใช้ CSS Scroll-Driven เมื่อ:
- animation ง่าย (fade, slide, scale)
- ต้องการ performance สูงสุด (runs on compositor thread)
- ไม่ต้องรองรับ browser เก่า
ใช้ GSAP เมื่อ:
- animation ซับซ้อน (pin, callback, sequence)
- ต้องรองรับ Safari 15 หรือ Firefox เก่า
- ต้องการ
onEnter,onLeavecallbacks
Browser Support
Chrome 115+, Edge 115+, Safari 18+ — รองรับ production ได้แล้วใน 2025+
/* @supports fallback */
@supports not (animation-timeline: scroll()) {
.animate-on-scroll { opacity: 1; transform: none; }
}
/* หรือ detect ด้วย JS ก่อนใส่ class */
if (CSS.supports('animation-timeline', 'scroll()')) {
document.body.classList.add('scroll-animations');
}
Reduce Motion
/* เคารพ prefers-reduced-motion เสมอ */
@media (prefers-reduced-motion: reduce) {
.animate-on-scroll {
animation: none;
opacity: 1;
transform: none;
}
}