"use client"; import { useEffect, useRef } from "react"; import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger); interface GsapRevealProps { children: React.ReactNode; variant?: "fade-up" | "fade-in" | "slide-left" | "slide-right" | "scale"; delay?: number; duration?: number; stagger?: number; className?: string; once?: boolean; } const VARIANTS = { "fade-up": { y: 40, opacity: 0 }, "fade-in": { opacity: 0 }, "slide-left": { x: -40, opacity: 0 }, "slide-right": { x: 40, opacity: 0 }, scale: { scale: 0.92, opacity: 0 }, } as const; export default function GsapReveal({ children, variant = "fade-up", delay = 0, duration = 0.8, stagger = 0, className = "", once = true, }: GsapRevealProps) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; // 关键降级:尊重用户系统设置,无障碍优先,不做任何位移。 const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (reduceMotion) return; const targets = el.children.length > 1 ? Array.from(el.children) : [el]; const ctx = gsap.context(() => { if (stagger > 0 && targets.length > 1) { targets.forEach((child, i) => { gsap.from(child, { ...VARIANTS[variant], duration, delay: delay + i * stagger, ease: "power3.out", scrollTrigger: { trigger: child, start: "top 92%", toggleActions: once ? "play none none none" : "play none none reverse", }, }); }); } else { gsap.from(targets, { ...VARIANTS[variant], duration, delay, ease: "power3.out", scrollTrigger: { trigger: el, start: "top 88%", toggleActions: once ? "play none none none" : "play none none reverse", }, }); } }, el); return () => ctx.revert(); }, [variant, delay, duration, stagger, once]); return (
{children}
); }