From add9733bc98707adb094931ec21e7883cd4fdf5a Mon Sep 17 00:00:00 2001 From: kurihada Date: Thu, 26 Feb 2026 16:41:39 +0800 Subject: [PATCH] =?UTF-8?q?ui:=20=E9=A1=B5=E9=9D=A2=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E8=BF=87=E6=B8=A1=E5=8A=A8=E7=94=BB=20=E2=80=94=20AnimatePrese?= =?UTF-8?q?nce=20=E6=B7=A1=E5=85=A5=E6=BB=91=E5=87=BA=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/layout.tsx | 3 ++- src/components/PageTransition.tsx | 45 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/components/PageTransition.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 25e975b..d0b1671 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,6 +3,7 @@ import { Geist } from "next/font/google"; import "./globals.css"; import GlobalUserBadge from "@/components/GlobalUserBadge"; import ServiceWorkerRegistrar from "@/components/ServiceWorkerRegistrar"; +import PageTransition from "@/components/PageTransition"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -40,7 +41,7 @@ export default function RootLayout({ - {children} + {children} ); diff --git a/src/components/PageTransition.tsx b/src/components/PageTransition.tsx new file mode 100644 index 0000000..35f0550 --- /dev/null +++ b/src/components/PageTransition.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { useContext, useRef, type PropsWithChildren } from "react"; +import { AnimatePresence, motion } from "framer-motion"; +import { usePathname } from "next/navigation"; +import { LayoutRouterContext } from "next/dist/shared/lib/app-router-context.shared-runtime"; + +/** + * Preserves the previous route's React context during the exit animation + * so the old page doesn't break while fading out. + */ +function FrozenRoute({ children }: PropsWithChildren) { + const ctx = useContext(LayoutRouterContext); + const frozen = useRef(ctx).current; + return ( + + {children} + + ); +} + +const variants = { + enter: { opacity: 0, y: 12 }, + center: { opacity: 1, y: 0 }, + exit: { opacity: 0, y: -12 }, +}; + +export default function PageTransition({ children }: PropsWithChildren) { + const pathname = usePathname(); + + return ( + + + {children} + + + ); +}