'use client'; import { motion, AnimatePresence } from 'framer-motion'; import { usePathname, useSearchParams } from 'next/navigation'; import { useEffect, useState, useRef } from 'react'; import { useRouter } from 'next/navigation'; interface PageTransitionProps { children: React.ReactNode; } export default function PageTransition({ children }: PageTransitionProps) { const pathname = usePathname(); const searchParams = useSearchParams(); const router = useRouter(); const [isNavigating, setIsNavigating] = useState(false); const [displayChildren, setDisplayChildren] = useState(children); const [pendingChildren, setPendingChildren] = useState(null); const navigationTimeoutRef = useRef(null); // 监听路由变化 useEffect(() => { // 当 pathname 或 searchParams 变化时,表示路由发生了变化 if (children !== displayChildren) { setPendingChildren(children); setIsNavigating(true); // 清除之前的超时 if (navigationTimeoutRef.current) { clearTimeout(navigationTimeoutRef.current); } // 模拟加载时间,让 exit 动画有足够时间执行 navigationTimeoutRef.current = setTimeout(() => { setDisplayChildren(children); setPendingChildren(null); setIsNavigating(false); }, 500); // 给 exit 动画 300ms + 缓冲时间 } }, [pathname, searchParams, children, displayChildren]); // 清理超时 useEffect(() => { return () => { if (navigationTimeoutRef.current) { clearTimeout(navigationTimeoutRef.current); } }; }, []); const getPageVariants = (direction: 'left' | 'right' | 'up' | 'down' = 'right') => { const directions = { left: { x: -100, y: 0 }, right: { x: 100, y: 0 }, up: { x: 0, y: -100 }, down: { x: 0, y: 100 } }; const exitDirections = { left: { x: 100, y: 0 }, right: { x: -100, y: 0 }, up: { x: 0, y: 100 }, down: { x: 0, y: -100 } }; return { initial: { opacity: 0, ...directions[direction], scale: 0.9, rotateX: -15 }, animate: { opacity: 1, x: 0, y: 0, scale: 1, rotateX: 0, transition: { duration: 0.5, ease: [0.25, 0.46, 0.45, 0.94], type: "spring", stiffness: 100, damping: 15 } }, exit: { opacity: 0, ...exitDirections[direction], scale: 0.9, rotateX: 15, transition: { duration: 0.3, ease: "easeIn" } } }; }; const getLoadingVariants = () => ({ initial: { opacity: 0, scale: 0.8, y: 20 }, animate: { opacity: 1, scale: 1, y: 0, transition: { duration: 0.3, ease: "easeOut" } }, exit: { opacity: 0, scale: 0.8, y: -20, transition: { duration: 0.2, ease: "easeIn" } } }); return ( <> {isNavigating && (
页面切换中...
)}
{displayChildren} ); }