From 629ed7b08d6b7ae33fb9687dd41db6493ce7ac22 Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 11 Mar 2026 19:40:42 +0000 Subject: [PATCH] Switch to version 1: modified src/components/cardStack/hooks/useCardAnimation.ts --- .../cardStack/hooks/useCardAnimation.ts | 208 +++++++++++++++--- 1 file changed, 180 insertions(+), 28 deletions(-) diff --git a/src/components/cardStack/hooks/useCardAnimation.ts b/src/components/cardStack/hooks/useCardAnimation.ts index 8b888d1..4331477 100644 --- a/src/components/cardStack/hooks/useCardAnimation.ts +++ b/src/components/cardStack/hooks/useCardAnimation.ts @@ -1,35 +1,187 @@ -'use client'; +import { useRef } from "react"; +import { useGSAP } from "@gsap/react"; +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import type { CardAnimationType, GridVariant } from "../types"; +import { useDepth3DAnimation } from "./useDepth3DAnimation"; -import { useEffect, useRef, useState } from 'react'; -import { UseCardAnimationReturn } from '@/components/cardStack/types'; +gsap.registerPlugin(ScrollTrigger); -export function useCardAnimation(): UseCardAnimationReturn { - const [isActive, setIsActive] = useState(false); - const [isMobile, setIsMobile] = useState(false); - const itemRefsArray = useRef<(HTMLElement | null)[]>([]); - const containerRef = useRef(null); - const perspectiveRef = useRef(null); - const bottomContentRef = useRef(null); +interface UseCardAnimationProps { + animationType: CardAnimationType | "depth-3d"; + itemCount: number; + isGrid?: boolean; + supports3DAnimation?: boolean; + gridVariant?: GridVariant; + useIndividualTriggers?: boolean; +} - useEffect(() => { - const checkMobile = () => { - setIsMobile(window.innerWidth < 768); - }; - checkMobile(); - window.addEventListener('resize', checkMobile); - return () => window.removeEventListener('resize', checkMobile); - }, []); +export const useCardAnimation = ({ + animationType, + itemCount, + isGrid = true, + supports3DAnimation = false, + gridVariant, + useIndividualTriggers = false +}: UseCardAnimationProps) => { + const itemRefs = useRef<(HTMLElement | null)[]>([]); + const containerRef = useRef(null); + const perspectiveRef = useRef(null); + const bottomContentRef = useRef(null); - const itemRefs = itemRefsArray.current.map((el) => ({ - current: el, - })) as React.RefObject[]; - - return { - isActive, - isMobile, + // Enable 3D effect only when explicitly supported and conditions are met + const { isMobile } = useDepth3DAnimation({ itemRefs, containerRef, perspectiveRef, - bottomContentRef, - }; -} + isEnabled: animationType === "depth-3d" && isGrid && supports3DAnimation && gridVariant === "uniform-all-items-equal", + }); + + // Use scale-rotate as fallback when depth-3d conditions aren't met + const effectiveAnimationType = + animationType === "depth-3d" && (isMobile || !isGrid || gridVariant !== "uniform-all-items-equal") + ? "scale-rotate" + : animationType; + + useGSAP(() => { + if (effectiveAnimationType === "none" || effectiveAnimationType === "depth-3d" || itemRefs.current.length === 0) return; + + const items = itemRefs.current.filter((el) => el !== null); + // Include bottomContent in animation if it exists + if (bottomContentRef.current) { + items.push(bottomContentRef.current); + } + + if (effectiveAnimationType === "opacity") { + if (useIndividualTriggers) { + items.forEach((item) => { + gsap.fromTo( + item, + { opacity: 0 }, + { + opacity: 1, + duration: 1.25, + ease: "sine", + scrollTrigger: { + trigger: item, + start: "top 80%", + toggleActions: "play none none none", + }, + } + ); + }); + } else { + gsap.fromTo( + items, + { opacity: 0 }, + { + opacity: 1, + duration: 1.25, + stagger: 0.15, + ease: "sine", + scrollTrigger: { + trigger: items[0], + start: "top 80%", + toggleActions: "play none none none", + }, + } + ); + } + } else if (effectiveAnimationType === "slide-up") { + items.forEach((item, index) => { + gsap.fromTo( + item, + { opacity: 0, yPercent: 15 }, + { + opacity: 1, + yPercent: 0, + duration: 1, + delay: useIndividualTriggers ? 0 : index * 0.15, + ease: "sine", + scrollTrigger: { + trigger: useIndividualTriggers ? item : items[0], + start: "top 80%", + toggleActions: "play none none none", + }, + } + ); + }); + } else if (effectiveAnimationType === "scale-rotate") { + if (useIndividualTriggers) { + items.forEach((item) => { + gsap.fromTo( + item, + { scaleX: 0, rotate: 10 }, + { + scaleX: 1, + rotate: 0, + duration: 1, + ease: "power3", + scrollTrigger: { + trigger: item, + start: "top 80%", + toggleActions: "play none none none", + }, + } + ); + }); + } else { + gsap.fromTo( + items, + { scaleX: 0, rotate: 10 }, + { + scaleX: 1, + rotate: 0, + duration: 1, + stagger: 0.15, + ease: "power3", + scrollTrigger: { + trigger: items[0], + start: "top 80%", + toggleActions: "play none none none", + }, + } + ); + } + } else if (effectiveAnimationType === "blur-reveal") { + if (useIndividualTriggers) { + items.forEach((item) => { + gsap.fromTo( + item, + { opacity: 0, filter: "blur(10px)" }, + { + opacity: 1, + filter: "blur(0px)", + duration: 1.2, + ease: "power2.out", + scrollTrigger: { + trigger: item, + start: "top 80%", + toggleActions: "play none none none", + }, + } + ); + }); + } else { + gsap.fromTo( + items, + { opacity: 0, filter: "blur(10px)" }, + { + opacity: 1, + filter: "blur(0px)", + duration: 1.2, + stagger: 0.15, + ease: "power2.out", + scrollTrigger: { + trigger: items[0], + start: "top 80%", + toggleActions: "play none none none", + }, + } + ); + } + } + }, [effectiveAnimationType, itemCount, useIndividualTriggers]); + + return { itemRefs, containerRef, perspectiveRef, bottomContentRef }; +};