Overview
Music Store uses GSAP (GreenSock Animation Platform) to create smooth, professional animations. Animations enhance the user experience with fade-ins, slide-ins, and staggered reveals.
Installation
Basic Usage Pattern
All animations follow a consistent pattern using GSAP context and React’s useEffect:
import { useEffect , useRef } from "react" ;
import gsap from "gsap" ;
function Component () {
const elementRef = useRef ( null );
useEffect (() => {
const ctx = gsap . context (() => {
// Animations here
});
return () => ctx . revert (); // Cleanup
}, []);
return < div ref = { elementRef } > Content </ div > ;
}
GSAP context (gsap.context()) is used for automatic cleanup and scoping. The cleanup function ctx.revert() is called when the component unmounts.
Hero Section Animations
The Hero component uses a timeline to orchestrate multiple animations:
src/components/Hero/Hero.jsx
import { useEffect , useRef } from "react" ;
import gsap from "gsap" ;
function Hero () {
const titleRef = useRef ( null );
const textRef = useRef ( null );
const btnRef = useRef ( null );
useEffect (() => {
const ctx = gsap . context (() => {
gsap . timeline ({ defaults: { ease: "power3.out" } })
. fromTo ( titleRef . current ,
{ y: 50 , opacity: 0 },
{ y: 0 , opacity: 1 , duration: 1.5 }
)
. fromTo ( textRef . current ,
{ y: 30 , opacity: 0 },
{ y: 0 , opacity: 1 , duration: 1.2 },
"-=1"
)
. fromTo ( btnRef . current ,
{ y: 20 , opacity: 0 },
{ y: 0 , opacity: 1 , duration: 1 },
"-=0.8"
);
});
return () => ctx . revert ();
}, []);
return (
< section >
< div ref = { titleRef } >
< h1 > Music Store </ h1 >
</ div >
< div ref = { textRef } >
< p > ENCUENTRA TU SONIDO IDEAL CON NOSOTROS </ p >
</ div >
< div ref = { btnRef } >
< button > Mirar Tienda </ button >
</ div >
</ section >
);
}
Timeline Breakdown
Title Animation Starts from y: 50, opacity: 0 and animates to visible position over 1.5s
Text Animation Begins 1 second before previous animation ends (-=1), creating overlap
Button Animation Starts 0.8s before text animation completes for smooth flow
Timeline Position Parameter
The third parameter in fromTo() controls timing:
. fromTo ( element , from , to , "-=1" ) // Start 1s before previous animation ends
. fromTo ( element , from , to , "+=0.5" ) // Start 0.5s after previous animation ends
. fromTo ( element , from , to ) // Start after previous animation completes
Category Page Animations
The Categoria component animates the sidebar and product grid:
import { useEffect , useRef } from "react" ;
import gsap from "gsap" ;
function Categoria ({ productos }) {
const asideRef = useRef ( null );
const gridRef = useRef ( null );
useEffect (() => {
const ctx = gsap . context (() => {
gsap . fromTo ( asideRef . current ,
{ x: - 30 , opacity: 0 },
{ x: 0 , opacity: 1 , duration: 1 , ease: "power3.out" , delay: 0.2 }
);
if ( gridRef . current && gridRef . current . children . length > 0 ) {
gsap . fromTo (
gridRef . current . children ,
{ y: 30 , opacity: 0 },
{ y: 0 , opacity: 1 , duration: 0.8 , ease: "power3.out" , stagger: 0.08 , delay: 0.3 }
);
}
});
return () => ctx . revert ();
}, [ nombre , productosFiltrados ]);
return (
< main >
< aside ref = { asideRef } >
{ /* Filter sidebar */ }
</ aside >
< div ref = { gridRef } >
{ productosFiltrados . map ( prod => (
< ProductCard key = { prod . id } { ... prod } />
)) }
</ div >
</ main >
);
}
Stagger Animation
The stagger property creates a cascading effect:
gsap . fromTo (
gridRef . current . children , // Animates all child elements
{ y: 30 , opacity: 0 },
{
y: 0 ,
opacity: 1 ,
duration: 0.8 ,
stagger: 0.08 // 0.08s delay between each child
}
);
0.08 : Each product card starts animating 0.08 seconds after the previous one
Creates a smooth wave effect across the grid
Works on any collection of elements (children, querySelectorAll results, etc.)
Product Detail Animations
The product detail page animates three sections with overlapping timing:
src/components/productos/ProductosDetalle.jsx
function ProductoDetalle ({ productos }) {
const imgRef = useRef ( null );
const infoRef = useRef ( null );
const descRef = useRef ( null );
useEffect (() => {
if ( ! producto ) return ;
const ctx = gsap . context (() => {
gsap . timeline ({ defaults: { ease: "power3.out" } })
. fromTo ( imgRef . current ,
{ x: - 40 , opacity: 0 },
{ x: 0 , opacity: 1 , duration: 1.2 }
)
. fromTo ( infoRef . current ,
{ x: 40 , opacity: 0 },
{ x: 0 , opacity: 1 , duration: 1.2 },
"-=1"
)
. fromTo ( descRef . current ,
{ y: 30 , opacity: 0 },
{ y: 0 , opacity: 1 , duration: 1 },
"-=0.6"
);
});
return () => ctx . revert ();
}, [ id , producto ]);
return (
< div >
< div ref = { imgRef } >
< img src = { producto . imagen } />
</ div >
< div ref = { infoRef } >
< h1 > { producto . nombre } </ h1 >
< p > $ { producto . precio } </ p >
</ div >
< div ref = { descRef } >
< p > { producto . descripcion } </ p >
</ div >
</ div >
);
}
Animation Flow
Image slides in from left (x: -40)
Info slides in from right (x: 40) starting 1s before image completes
Description fades up from below (y: 30) starting 0.6s before info completes
Common Animation Properties
fromTo() Method
gsap . fromTo ( element , fromVars , toVars , position )
fromVars Starting state: { x: -30, opacity: 0 }
toVars Ending state: { x: 0, opacity: 1, duration: 1 }
position Timeline position: "-=1", "+=0.5", or omit
ease Easing function: "power3.out", "elastic", "bounce", etc.
Common Easing Functions
// Smooth deceleration (most common)
{ ease : "power3.out" }
// Smooth acceleration
{ ease : "power3.in" }
// Smooth acceleration and deceleration
{ ease : "power3.inOut" }
// Bouncy effect
{ ease : "bounce.out" }
// Elastic spring effect
{ ease : "elastic.out(1, 0.3)" }
Re-animation on Changes
Animations can re-trigger when dependencies change:
useEffect (() => {
const ctx = gsap . context (() => {
// Animations
});
return () => ctx . revert ();
}, [ nombre , productosFiltrados ]); // Re-run when these change
When nombre (category) or productosFiltrados changes, the cleanup function runs first (reverting animations), then new animations are applied.
Best Practices
Always Use Refs
// ✅ Correct - using ref
const elementRef = useRef ( null );
gsap . fromTo ( elementRef . current , { ... }, { ... });
// ❌ Wrong - direct DOM query
gsap . fromTo ( ".my-class" , { ... }, { ... });
Clean Up with Context
// ✅ Correct - automatic cleanup
const ctx = gsap . context (() => {
gsap . fromTo ( ... );
});
return () => ctx . revert ();
// ❌ Wrong - no cleanup
gsap . fromTo ( ... );
Check Element Existence
// ✅ Correct - check before animating
if ( gridRef . current && gridRef . current . children . length > 0 ) {
gsap . fromTo ( gridRef . current . children , ... );
}
// ✅ Correct - early return
if ( ! producto ) return ;
gsap . fromTo ( ... );
Use Transforms Prefer x, y, scale over left, top, width for better performance
Batch Animations Use timelines and stagger instead of individual animations
Set Defaults Use timeline defaults for repeated properties: { defaults: { ease: "power3.out" } }
Revert on Unmount Always clean up with ctx.revert() to prevent memory leaks