A scroll-triggered card stack animation component that displays cards fanning out and animating in from below.
Installation
Install dependencies:
npm install gsap @gsap/reactCopy the component:
"use client"
import { useRef } from "react"
import { useGSAP } from "@gsap/react"
import gsap from "gsap"
import { ScrollTrigger } from "gsap/ScrollTrigger"
gsap.registerPlugin(ScrollTrigger)
const CardStackAnimate = () => {
const first = useRef(null)
useGSAP(() => {
const tl = gsap.timeline({})
const cards = gsap.utils.toArray(".card")
tl.from(cards, {
y: "100vh",
stagger: 1,
ease: "power3.out",
duration: 1,
scrollTrigger: {
trigger: first.current,
start: "top top",
end: "2000",
scrub: 1,
pin: true,
},
})
})
const colors = [
{ name: "Pink", class: "bg-pink-300", link: "/card_animation/pink.png" },
{ name: "Blue", class: "bg-blue-300", link: "/card_animation/blue.png" },
{
name: "Yellow",
class: "bg-yellow-200",
link: "/card_animation/yellow.png",
},
{ name: "Green", class: "bg-green-200", link: "/card_animation/green.png" },
{
name: "Purple",
class: "bg-purple-200",
link: "/card_animation/purple.png",
},
{
name: "Orange",
class: "bg-orange-200",
link: "/card_animation/orange.png",
},
]
return (
<div className="h-full w-full bg-black">
<div
ref={first}
className="relative flex h-screen w-full items-center justify-center bg-black"
>
<div className="text-9xl font-light text-white">HEY GSAPPPPP</div>
{colors.map((color, i) => (
<div
key={i}
className={`card absolute h-[400px] w-[300px] rounded-3xl shadow-lg ${color.class} overflow-hidden p-4`}
style={{
left: `calc(50% + (${i * 70}px - ${(colors.length / 2) * 70}px))`,
transform: `translateX(-50%) rotate(${(i - colors.length / 2) * 10}deg)`,
}}
>
<div className="flex justify-between">
<div className="text-xs text-neutral-700">Card {i + 1}</div>
<div className="text-xs text-neutral-700">Color {color.class}</div>
</div>
<img src={color.link} alt={color.name} className="absolute left-0" />
</div>
))}
</div>
<div className="flex h-screen w-full items-center justify-center">hi</div>
</div>
)
}
export default CardStackAnimate
Update imports to match your project structure.
Usage
import CardStackAnimate from "@/components/ui/cards"<CardStackAnimate />Features
- Scroll-triggered animations - Cards animate based on scroll position
- Staggered entrance - Each card enters sequentially for a polished effect
- Pin effect - Scroll triggers pinning for controlled animation timing
- Customizable colors - Easy color variants for each card
- Responsive layout - Cards fan out dynamically based on container
Customization
Adding More Colors
Extend the colors array to add more card variants:
const colors = [
{ name: "Red", class: "bg-red-300", link: "/card_animation/red.png" },
{ name: "Teal", class: "bg-teal-300", link: "/card_animation/teal.png" },
// ... add more colors
]Adjusting Animation Speed
Modify the duration and stagger values:
tl.from(cards, {
y: "100vh",
stagger: 0.5, // Reduce for faster stagger
duration: 2, // Increase for slower animation
ease: "power3.out",
// ...
})Changing Scroll Trigger Duration
Adjust the end value to control how long the animation scrubs:
scrollTrigger: {
trigger: first.current,
start: "top top",
end: "3000", // Increase for longer scroll distance
scrub: 1,
pin: true,
}Customizing Card Dimensions
Update the size classes on the card div:
className={`card absolute h-[500px] w-[400px] rounded-3xl shadow-lg ...`}