用途:在 Next.js App Router 中实现页面进入/退出过渡动画,并修复最常见的失败——Framer Motion 的 AnimatePresence 退出动画在路由导航时从不触发。原因是结构性的:App Router 导航时会几乎立即卸载旧路由内容并挂载新路由,AnimatePresence 只有在退出元素仍挂载于持久 AnimatePresence 包装器下时才可能播放退出动画,因此退出被跳过。
主要能力:提供两种解决方案。方案 A 使用 app/template.tsx(每次导航重新挂载,区别于持久化的 layout.tsx)+ 以 pathname 为 key 的 AnimatePresence + FrozenRouter。FrozenRouter 通过快照 LayoutRouterContext,让正在退出的子树在退出动画期间继续渲染旧内容,避免闪现新路由内容。关键点:mode="wait" 让退出先完成再挂载新页;initial={false} 跳过首屏进入动画;key={pathname} 让 AnimatePresence 识别路由切换。方案 B 使用原生 View Transitions API 或 next-view-transitions 库,通过 ::view-transition-old/new(root) 与 document.startViewTransition 实现跨页淡入淡出或共享元素过渡。
使用方式:Framer Motion 方案需在每个使用 AnimatePresence/usePathname/motion 的文件加 'use client'。View Transitions 方案在 layout.tsx 用 ViewTransitions 包裹,并使用库的 Link 替代 next/link;SPA 导航需用 if (document.startViewTransition) 守卫包裹 router.push。
适用场景:需要逐路由进入动画、导航退出动画、简单跨页淡入淡出/形变、共享元素过渡时。选择建议:简单淡入淡出/形变用 View Transitions;需要细粒度、可编排、可中断或错峰退出动画时用 Framer Motion。
限制:LayoutRouterContext 是 Next.js 内部实现,导入路径可能随版本变化;View Transitions API 浏览器支持不完整,必须提供回退;layout.tsx 不会逐路由重新挂载;缺少 key={pathname} 或 FrozenRouter 会导致退出不触发或内容闪现;mode="wait" 在退出过慢时会让导航显得卡顿,建议时长约 0.2–0.4 秒;需尊重 prefers-reduced-motion。资料还提供独立 HTML 验证方式与截图脚本,但真实目标仍是 Next.js 应用。