Skip to main content
The GuitarOfTheMonth component creates a premium product showcase with scroll-triggered animations and interactive 3D tilt effects.

Overview

This component features:
  • Scroll-triggered sequential animations
  • Interactive 3D tilt effect on mouse movement
  • Premium product presentation layout
  • Radial gradient spotlight effect

Implementation

GuitarOfTheMonth.jsx
import { useEffect, useRef } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

function GuitarOfTheMonth() {
  const guitarRef = useRef(null);
  const sectionRef = useRef(null);
  const labelRef = useRef(null);
  const titleRef = useRef(null);
  const descRef = useRef(null);
  const priceRef = useRef(null);
  const btnRef = useRef(null);
  const imgWrapRef = useRef(null);

  useEffect(() => {
    const section = sectionRef.current;
    if (!section) return;

    const ctx = gsap.context(() => {
      gsap.timeline({
        defaults: { ease: "power3.out" },
        scrollTrigger: { trigger: section, start: "top 75%" }
      })
        .fromTo(labelRef.current, 
          { y: 20, opacity: 0 }, 
          { y: 0, opacity: 1, duration: 0.8 }
        )
        .fromTo(titleRef.current, 
          { y: 40, opacity: 0 }, 
          { y: 0, opacity: 1, duration: 1.2 }, 
          "-=0.4"
        )
        .fromTo(descRef.current, 
          { y: 30, opacity: 0 }, 
          { y: 0, opacity: 1, duration: 1 }, 
          "-=0.8"
        )
        .fromTo(priceRef.current, 
          { y: 20, opacity: 0 }, 
          { y: 0, opacity: 1, duration: 0.8 }, 
          "-=0.6"
        )
        .fromTo(btnRef.current, 
          { y: 20, opacity: 0 }, 
          { y: 0, opacity: 1, duration: 0.8 }, 
          "-=0.6"
        )
        .fromTo(imgWrapRef.current, 
          { x: 60, opacity: 0 }, 
          { x: 0, opacity: 1, duration: 1.4 }, 
          "-=1.2"
        );
    });

    const handleMouseMove = (e) => {
      const rect = section.getBoundingClientRect();
      const xPercent = ((e.clientX - rect.left) / rect.width - 0.5) * 2;
      const yPercent = ((e.clientY - rect.top) / rect.height - 0.5) * 2;
      gsap.to(guitarRef.current, {
        rotateY: xPercent * 20,
        rotateX: -yPercent * 8,
        duration: 0.8,
        ease: "power2.out",
        transformPerspective: 1000,
      });
    };

    const handleMouseLeave = () => {
      gsap.to(guitarRef.current, {
        rotateY: 0, 
        rotateX: 0,
        duration: 1, 
        ease: "power3.out",
      });
    };

    section.addEventListener("mousemove", handleMouseMove);
    section.addEventListener("mouseleave", handleMouseLeave);
    
    return () => {
      ctx.revert();
      section.removeEventListener("mousemove", handleMouseMove);
      section.removeEventListener("mouseleave", handleMouseLeave);
    };
  }, []);

  return (
    <section ref={sectionRef} className="relative bg-[#0a0a0a] py-32 px-6">
      {/* Radial gradient spotlight */}
      <div className="absolute inset-0 pointer-events-none"
        style={{
          background: "radial-gradient(ellipse 60% 50% at 60% 50%, rgba(255,255,255,0.04) 0%, transparent 70%)"
        }}
      />
      
      <div className="max-w-6xl mx-auto flex flex-col md:flex-row items-center gap-16">
        {/* Product info */}
        <div className="md:w-1/3 text-white pl-8 md:pl-16">
          <p ref={labelRef} className="text-xs uppercase text-white/40 mb-4">
            Guitarra del mes
          </p>
          <h2 ref={titleRef} className="text-5xl font-bold mb-6">
            Gibson<br />Les Paul
          </h2>
          <p ref={descRef} className="text-white/50 mb-8">
            Un clásico atemporal. El sonido que definió décadas de rock, blues y soul.
          </p>
          <p ref={priceRef} className="text-2xl font-semibold mb-8">
            $16,000,000
          </p>
          <button ref={btnRef} className="px-8 py-3 border border-white/20 rounded-full">
            Ver Producto en detalle
          </button>
        </div>

        {/* Product image with 3D effect */}
        <div ref={imgWrapRef} className="md:w-2/3 relative flex justify-center">
          <img
            ref={guitarRef}
            src="/img/GuitarraDelMes.png"
            alt="Gibson Les Paul"
            className="relative z-10 select-none"
            style={{
              maxHeight: "480px",
              transformStyle: "preserve-3d",
              filter: "drop-shadow(0 40px 60px rgba(0,0,0,0.8))"
            }}
            draggable={false}
          />
        </div>
      </div>
    </section>
  );
}

export default GuitarOfTheMonth;

Animation Features

Scroll-Triggered Timeline

Elements animate in sequence when the section enters viewport:
  1. Label (0.8s) - “Guitarra del mes” fades in
  2. Title (1.2s) - Product name appears
  3. Description (1.0s) - Product details fade in
  4. Price (0.8s) - Price tag animates
  5. Button (0.8s) - CTA button appears
  6. Image (1.4s) - Product image slides in from right
All animations use power3.out easing and overlap for smooth flow.

3D Tilt Effect

The guitar image responds to mouse movement:
const xPercent = ((e.clientX - rect.left) / rect.width - 0.5) * 2;
const yPercent = ((e.clientY - rect.top) / rect.height - 0.5) * 2;

gsap.to(guitarRef.current, {
  rotateY: xPercent * 20,  // Max ±20° horizontal rotation
  rotateX: -yPercent * 8,  // Max ±8° vertical rotation
  duration: 0.8,
  transformPerspective: 1000,
});
  • Rotation based on mouse position relative to section
  • Smooth animation with 0.8s duration
  • Returns to neutral position on mouse leave

Styling

Layout

  • Two-column responsive layout (stacks on mobile)
  • Product info: 1/3 width
  • Product image: 2/3 width
  • Centered vertical alignment

Visual Effects

  • Radial gradient spotlight for depth
  • Drop shadow on guitar image
  • 3D transform perspective
  • Glassmorphic button with border

Usage

Body.jsx
import GuitarOfTheMonth from '../components/GuitarOfTheMth/GuitarOfTheMonth';
import Squares from '../components/Squares/Squares';

function Body() {
  return (
    <section className="relative w-full overflow-hidden py-32">
      <div className="absolute inset-0 z-0">
        <Squares speed={0.5} squareSize={40} direction="diagonal" />
      </div>
      <div className="relative z-10">
        <GuitarOfTheMonth />
      </div>
    </section>
  );
}
The 3D tilt effect requires preserve-3d transform style and proper perspective settings for optimal visual quality.
Pair this component with the Squares background for a premium, layered visual effect.

Build docs developers (and LLMs) love