Stagger & Complex Sequences
Stagger animation adalah teknik menganimasi sekumpulan elemen dengan delay yang berurutan, menciptakan efek "domino" yang terasa elegan dan terorganisir. Sequence adalah kombinasi beberapa animasi yang berjalan berurutan atau paralel.
Stagger Dasar
/* CSS — stagger manual dengan animation-delay */
.item:nth-child(1) { animation-delay: 0ms; }
.item:nth-child(2) { animation-delay: 100ms; }
.item:nth-child(3) { animation-delay: 200ms; }
.item:nth-child(4) { animation-delay: 300ms; }
@keyframes fadeUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.item { animation: fadeUp 0.5s ease-out both; }
// JavaScript — stagger programatik
function staggerIn(elements, staggerMs = 100) {
elements.forEach((el, i) => {
el.style.animationDelay = \`${i * staggerMs}ms\`;
el.classList.add('animate-in');
});
}
// Framer Motion — paling mudah
<motion.ul>
{items.map((item, i) => (
<motion.li
key={item.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
delay: i * 0.1,
duration: 0.4,
ease: 'easeOut',
}}
>
{item.label}
</motion.li>
))}
</motion.ul>
Stagger dengan Variants (Framer Motion)
Variants memungkinkan stagger yang lebih deklaratif dan powerful:
const containerVariants = {
hidden: {},
visible: {
transition: {
staggerChildren: 0.1, // 100ms antar child
delayChildren: 0.2, // 200ms sebelum child pertama
staggerDirection: 1, // 1 = dari awal, -1 = dari akhir
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: 'easeOut' },
},
};
function AnimatedList({ items }) {
return (
<motion.ul
variants={containerVariants}
initial="hidden"
animate="visible"
>
{items.map((item) => (
<motion.li key={item.id} variants={itemVariants}>
{item.label}
</motion.li>
))}
</motion.ul>
);
}
Stagger dari Tengah (from: center)
// GSAP — stagger dari tengah ke tepi
gsap.from('.grid-item', {
opacity: 0,
scale: 0.5,
duration: 0.5,
stagger: {
each: 0.05,
from: 'center', // 'start' | 'end' | 'center' | 'random'
},
});
// Manual — hitung delay dari center
function staggerFromCenter(elements, staggerMs) {
const center = (elements.length - 1) / 2;
return elements.map((el, i) => ({
element: el,
delay: Math.abs(i - center) * staggerMs,
}));
}
Complex Sequences
// Framer Motion — sequence dengan useAnimate
import { useAnimate } from 'motion/react';
function IntroAnimation() {
const [scope, animate] = useAnimate();
async function playIntro() {
// Sequence — berjalan berurutan
await animate('.overlay', { opacity: 0 }, { duration: 0.5 });
await animate('.title', { opacity: 1, y: 0 }, { duration: 0.6 });
await animate('.subtitle', { opacity: 1, y: 0 }, { duration: 0.5, delay: 0.1 });
// Stagger items
await animate('.item', { opacity: 1, x: 0 }, {
duration: 0.4,
delay: stagger(0.1),
});
}
return <div ref={scope}>...</div>;
}
Orchestrasi dengan Promise
// Menggabungkan beberapa animasi
async function runSequence() {
// Paralel
await Promise.all([
animate('.left', { x: 0 }),
animate('.right', { x: 0 }),
]);
// Berurutan
await animate('.center', { scale: 1 });
await animate('.content', { opacity: 1 });
}
Tips Stagger yang Efektif
- Durasi stagger — 50-150ms biasanya optimal. Terlalu cepat: tidak terasa. Terlalu lambat: menggeser user terlalu lama.
- Jumlah elemen — semakin banyak elemen, semakin kecil stagger per item (agar total waktu tidak terlalu lama)
- Arah stagger — pilih arah yang logis (kiri ke kanan untuk list horizontal, atas ke bawah untuk list vertikal)
- Exit stagger — saat elemen keluar, gunakan stagger terbalik (dari akhir ke awal)
Stagger adalah tools yang sederhana tapi punya dampak besar pada kualitas visual animasi — buat list terasa hidup, bukan robotic.