如何在React Router DOM 6.8+中结合Framer Motion实现路由退出动画?
解决createBrowserRouter搭配AnimatePresence实现路由退出动画的问题
在React Router v6+使用createBrowserRouter时,不能直接把AnimatePresence作为路由组件或包裹RouterProvider,正确的做法是在**根布局组件(RootLayout)**中结合useLocation钩子来实现。
核心解决方案步骤
- 在根布局中通过
useLocation获取当前路由的location.key(每个路由切换时这个key会更新,用来触发AnimatePresence的退出逻辑) - 用
AnimatePresence包裹<Outlet />,并为包裹Outlet的容器添加location.key作为唯一标识 - 为路由内容添加
motion组件及动画属性(initial/animate/exit)
完整代码示例
1. 修改RootLayout组件
import { Outlet, useLocation } from 'react-router-dom'; import { AnimatePresence, motion } from 'framer-motion'; function RootLayout() { const location = useLocation(); return ( <div className="root-layout"> {/* 这里放置导航栏、页脚等公共布局内容 */} <AnimatePresence exitBeforeEnter> {/* 用location.key作为key,确保路由切换时触发动画 */} <motion.div key={location.key} initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -20 }} transition={{ duration: 0.3 }} > <Outlet /> </motion.div> </AnimatePresence> </div> ); }
2. 保持原有路由配置不变
const router = createBrowserRouter( createRoutesFromElements( <Route path="/" element={<RootLayout />}> <Route index element={<Main />} /> <Route path="about" element={<About />} /> <Route path="projects" element={<Projects />} /> <Route path="contact" element={<Contact />} /> </Route> ) ); function App() { return <RouterProvider router={router} />; }
为什么之前的尝试失败?
- 包裹RouterProvider外:
RouterProvider是React Router的顶层入口,AnimatePresence无法在这里感知路由变化的唯一标识(key),因此无法触发退出动画。 - 直接包裹Outlet但未加key:AnimatePresence需要通过唯一key识别组件的挂载/卸载,没有key的话,Outlet切换内容只会更新内部DOM,不会触发退出逻辑。
- 单独使用motion+useLocation但未包AnimatePresence:
exit动画属性必须在AnimatePresence容器内才能生效,它专门负责监听组件的卸载并执行退出动画。
扩展:自定义单页动画
如果需要给不同页面设置不同的动画效果,可以把motion.div放到每个路由组件内部,RootLayout只负责提供AnimatePresence容器:
示例:Main组件自定义动画
import { motion } from 'framer-motion'; function Main() { return ( <motion.div initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.9 }} transition={{ duration: 0.4 }} > {/* 主页内容 */} </motion.div> ); }
对应修改RootLayout
function RootLayout() { const location = useLocation(); return ( <div className="root-layout"> <AnimatePresence exitBeforeEnter> <div key={location.key}> <Outlet /> </div> </AnimatePresence> </div> ); }
内容的提问来源于stack exchange,提问作者Wise.Wolf
相关产品推荐
相关产品推荐

