"use client"; import { useLayoutEffect, useRef } from "react"; import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import Button from "@/components/ui/Button"; import ImageOrVideo from "@/components/ui/ImageOrVideo"; import { cls } from "@/lib/utils"; gsap.registerPlugin(ScrollTrigger); type FeatureItem = { title: string; description: string; primaryButton?: { text: string; href: string }; secondaryButton?: { text: string; href: string }; } & ( | { leftImageSrc: string; leftVideoSrc?: never } | { leftVideoSrc: string; leftImageSrc?: never } ) & ( | { rightImageSrc: string; rightVideoSrc?: never } | { rightVideoSrc: string; rightImageSrc?: never } ); interface FeaturesStickyCardsProps { items: FeatureItem[]; } const CardFrame = ({ imageSrc, videoSrc, cardRef, className = "", }: { imageSrc?: string; videoSrc?: string; cardRef: (el: HTMLDivElement | null) => void; className?: string; }) => (
); const FeaturesStickyCards = ({ items, }: FeaturesStickyCardsProps) => { const imageRefs = useRef<(HTMLDivElement | null)[]>([]); const mobileImageRefs = useRef<(HTMLDivElement | null)[]>([]); const triggerRefs = useRef<(HTMLDivElement | null)[]>([]); useLayoutEffect(() => { const mm = gsap.matchMedia(); const getAnimationConfig = (itemIndex: number, isLeftCard: boolean) => { const isOddItem = itemIndex % 2 === 1; if (isLeftCard) { return { from: { xPercent: -225, rotation: -45 }, to: { rotation: isOddItem ? 10 : -10 }, }; } else { return { from: { xPercent: 225, rotation: 45 }, to: { rotation: isOddItem ? -10 : 10 }, }; } }; const animateCards = (isMobile: boolean) => { items.forEach((_, itemIndex) => { [0, 1].forEach((cardIndex) => { const refIndex = itemIndex * 2 + cardIndex; const element = isMobile ? mobileImageRefs.current[refIndex] : imageRefs.current[refIndex]; if (element) { const isLeftCard = cardIndex === 0; const fromConfig = isMobile ? { xPercent: isLeftCard ? -150 : 150, rotation: isLeftCard ? -25 : 25, } : getAnimationConfig(itemIndex, isLeftCard).from; const toConfig = isMobile ? { xPercent: 0, rotation: 0, duration: 1, scrollTrigger: { trigger: element, start: "top 90%", end: "top 50%", scrub: 1, }, } : { xPercent: 0, rotation: getAnimationConfig(itemIndex, isLeftCard).to.rotation, scrollTrigger: { trigger: triggerRefs.current[itemIndex], start: "top bottom", end: "top top", scrub: 1, }, }; gsap.fromTo(element, fromConfig, toConfig); } }); }); }; mm.add("(max-width: 767px)", () => animateCards(true)); mm.add("(min-width: 768px)", () => animateCards(false)); return () => { mm.revert(); imageRefs.current = []; mobileImageRefs.current = []; triggerRefs.current = []; }; }, [items]); const sectionHeightStyle = { height: `${items.length * 100}vh` }; return (
{items.map((item, index) => (
{ triggerRefs.current[index] = el; }} className="w-full mx-auto h-screen flex justify-center items-center" >

{index + 1}

{item.title}

{item.description}

{(item.primaryButton || item.secondaryButton) && (
{item.primaryButton &&
)}
))}
{items.map((item, itemIndex) => (
{ imageRefs.current[itemIndex * 2] = el; }} className="w-25/100 xl:w-27/100 2xl:w-29/100 h-[70vh]" /> { imageRefs.current[itemIndex * 2 + 1] = el; }} className="w-25/100 xl:w-27/100 2xl:w-28/100 h-[70vh]" />
))}
{items.map((item, itemIndex) => (

{itemIndex + 1}

{item.title}

{item.description}

{(item.primaryButton || item.secondaryButton) && (
{item.primaryButton &&
)}
{ mobileImageRefs.current[itemIndex * 2] = el; }} className="w-1/2 aspect-9/16" /> { mobileImageRefs.current[itemIndex * 2 + 1] = el; }} className="w-1/2 aspect-9/16" />
))}
); }; export default FeaturesStickyCards;