Framer Motion的AnimationPresence失效,已检查定位与key属性
解决Framer Motion退出动画不生效问题
核心原因
你猜的没错,问题根源就是<a>标签的硬跳转:浏览器会直接刷新页面,Framer Motion的AnimatePresence根本来不及执行退出动画。而NextJS的<Link>是客户端路由,不会刷新整个页面,能让AnimatePresence检测到页面组件的卸载,从而触发exit动画。
可行解决方案
1. 给自定义ShuffleText套,保留hover效果
不用放弃<Link>,通过passHref属性把路由传递给内部的<a>,再重置<a>的默认样式,让ShuffleText完全控制交互:
import Link from 'next/link'; const ShuffleTextLink = ({ href, children }) => { return ( <Link href={href} passHref> <a style={{ textDecoration: 'none', color: 'inherit' }}> <ShuffleText>{children}</ShuffleText> </a> </Link> ); };
之后用这个ShuffleTextLink替代原来的<a>标签即可。
2. 手动拦截跳转,用router.push实现客户端导航
如果一定要用原生<a>标签,阻止默认跳转行为,改用NextJS的router.push做客户端路由:
import { useRouter } from 'next/router'; const CustomShuffleLink = ({ href, children }) => { const router = useRouter(); const handleNav = (e) => { e.preventDefault(); router.push(href); }; return ( <a href={href} onClick={handleNav}> <ShuffleText>{children}</ShuffleText> </a> ); };
这样和<Link>的客户端路由效果一致,AnimatePresence能正常捕获路由变化。
3. 检查AnimatePresence的配置细节
确保你的AnimatePresence配置满足以下要求:
- 包裹在所有页面组件的外层,且在
_app.js中不要被其他会卸载的组件包裹 - 使用
router.asPath作为key(比router.route更准确,能捕获动态路由参数) - 页面组件的motion容器必须定义
exit动画变体,同时建议开启mode="wait"确保退出动画完成后再加载新页面:
// _app.jsx import { motion, AnimatePresence } from 'framer-motion'; import { useRouter } from 'next/router'; function MyApp({ Component, pageProps }) { const router = useRouter(); return ( <AnimatePresence mode="wait"> <motion.div key={router.asPath} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.4 }} > <Component {...pageProps} /> </motion.div> </AnimatePresence> ); }
为什么部分场景生效?
从/writing到/writing/[slug]用了<Link>,属于客户端路由跳转,页面组件是被卸载而非整个页面刷新,所以AnimatePresence能触发exit动画;而从/到/writing用了<a>硬跳转,浏览器直接重新加载页面,AnimatePresence还没来得及执行退出逻辑就被销毁了,所以动画不生效。
内容的提问来源于stack exchange,提问作者SnooDucks299792
相关产品推荐
相关产品推荐

