Next.js 14中Framer Motion页面退出动画不触发的问题排查
问题原因及修复方案
核心问题:AnimatePresence作用范围错误
你把PageTransitionLayout放在单个页面组件内部,导航切换页面时,整个页面组件(包括内部的AnimatePresence)会被直接卸载,AnimatePresence根本没有机会触发子元素的退出动画——它自己都先被销毁了。
具体修复步骤
1. 将过渡布局提升到根布局(共享层级)
在Next.js 14 App Router中,AnimatePresence必须放在不会随页面切换卸载的父组件里(比如app/layout.tsx),这样页面切换时它能持续存在,监听到元素替换并触发动画。
修改根布局app/layout.tsx:
'use client'; import { motion, AnimatePresence } from "framer-motion"; import { usePathname } from "next/navigation"; import type { ReactNode } from "react"; export default function RootLayout({ children }: { children: ReactNode }) { const pathname = usePathname(); return ( <html lang="zh-CN"> <body> <AnimatePresence mode="wait"> {/* 页面内容容器,绑定pathname作为key触发切换 */} <motion.div key={pathname} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.5 }} className="relative z-10" > {children} </motion.div> {/* 全屏过渡遮罩元素 */} <motion.div key={`mask-${pathname}`} className="fixed top-0 left-0 w-full h-screen bg-green-400 origin-middle z-20" initial={{ scaleY: 1 }} animate={{ scaleY: 0.5 }} exit={{ scaleY: 0 }} transition={{ duration: 1, ease: [0.22, 1, 0.36, 1] }} /> </AnimatePresence> </body> </html> ); }
2. 移除页面内部的过渡布局包裹
现在app/contact/page.tsx无需再嵌套PageTransitionLayout,直接编写页面内容即可:
export default function Contact() { return ( <div className="grid h-[90vh] place-items-center bg-orange-400"> <h1 className="font-bold text-4xl">Contact</h1> </div> ); }
3. 修正动画细节
- 用
fixed替代absolute作为遮罩定位,避免布局层级混乱 - 给页面容器和遮罩设置
z-index,确保遮罩在页面上方显示过渡效果 - 页面容器的
exit动画可与遮罩动画配合,实现更自然的切换体验
额外注意点
- key值唯一性:必须用
pathname作为元素key,确保页面切换时AnimatePresence能识别元素替换 - 客户端组件标识:根布局需添加
'use client',保证Framer Motion的客户端API正常运行
内容的提问来源于stack exchange,提问作者Dagem
相关产品推荐
相关产品推荐

