React Framer-motion AnimatePresence退出动画失效问题排查
问题描述
我正在尝试实现页面转场动画,但仅入场动画正常生效。文本可顺利滑入,但路由切换至其他组件时,文本不会执行滑出动画而是直接消失。已尝试为单个组件及Outlet组件分配基于location.pathname的key,也为AnimatePresence添加了exitBeforeEnter与mode="wait"配置,仍无法定位问题所在。
相关代码
主组件
import React from "react"; import Navbar from './navbar.js'; import { Outlet } from "react-router-dom" import { AnimatePresence } from "framer-motion" export default function App() { return ( <div> <Navbar/> <AnimatePresence> <Outlet/> </AnimatePresence> </div> ); }
页面组件示例(Section1)
import React from 'react'; import './index.css' import { motion } from "framer-motion" import { useLocation } from 'react-router-dom'; export default function Section1() { let location = useLocation() return ( <motion.p key={location.pathname} transition={{duration: 0.8, ease: "easeOut"}} initial={{ x: "-100%", opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: "100%", opacity: 0 }} className='text-center text-6xl my-56'>Section 1</motion.p> ) }
导航栏组件
import React from "react" import { Link } from "react-router-dom"; export default function Navbar(router) { return ( <nav className='bg-gray-300 flex items-center justify-between'> <p className="ml-6 text-3xl">Варвара Алексеева</p> <ul className='flex flex-row justify-end mr-20 text-xl'> <li className='m-4'><Link to={"/"} className='hover:bg-lapis hover:text-white p-2 rounded-xl duration-300'>Main</Link></li> <li className='m-4'><Link to={"/section1"} className='hover:bg-lapis hover:text-white p-2 rounded-xl duration-300'>Section 1</Link></li> <li className='m-4'><Link to={"/section2"} className='hover:bg-lapis hover:text-white p-2 rounded-xl duration-300'>Section-2 2</Link></li> <li className='m-4'><Link to={"/section3"} className='hover:bg-lapis hover:text-white p-2 rounded-xl duration-300'>Section 3</Link></li> </ul> </nav> ) }
解决方案
核心问题是**AnimatePresence需要通过唯一key识别元素的替换,才能触发退出动画**,之前的写法里Outlet没有绑定路由相关的key,导致路由切换时AnimatePresence无法追踪旧组件的卸载过程。
修改步骤
为主组件的Outlet添加路由key
在主组件中引入useLocation获取当前路由信息,给Outlet设置key={location.pathname},同时给AnimatePresence加上exitBeforeEnter确保退出动画完成后再执行入场动画:import React from "react"; import Navbar from './navbar.js'; import { Outlet, useLocation } from "react-router-dom" import { AnimatePresence } from "framer-motion" export default function App() { const location = useLocation(); return ( <div> <Navbar/> <AnimatePresence exitBeforeEnter> <Outlet key={location.pathname}/> </AnimatePresence> </div> ); }移除页面组件中多余的key
页面组件里的motion元素不需要再绑定location.pathname作为key,因为Outlet已经通过key触发了组件的替换,AnimatePresence会自动捕获到exit事件:import React from 'react'; import './index.css' import { motion } from "framer-motion" export default function Section1() { return ( <motion.p transition={{duration: 0.8, ease: "easeOut"}} initial={{ x: "-100%", opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: "100%", opacity: 0 }} className='text-center text-6xl my-56'>Section 1</motion.p> ) }确保所有页面组件的motion元素都配置exit属性
每个路由对应的页面组件,其根motion元素必须包含exit动画配置,这样AnimatePresence才能正确触发退出动画。
原理说明
AnimatePresence的工作逻辑是:当包裹的子元素发生替换(通过key变化识别)时,先执行旧元素的exit动画,待动画完成后再卸载旧元素并渲染新元素执行入场动画。之前的写法中Outlet没有key,路由切换时AnimatePresence感知不到元素的替换,直接卸载旧组件,因此看不到退出动画。给Outlet绑定路由pathname作为key后,每次路由变化都会触发Outlet的“新旧替换”,AnimatePresence就能按预期执行完整的转场动画。
内容的提问来源于stack exchange,提问作者mcmikey

