Next.js14+Framer Motion路由页面过渡问题求助
问题解决方案
问题1:Parallel Routes模态框打开/关闭时背景页面过渡动画重叠
核心问题是模态框路由切换时,背景页面的动画逻辑判断不准确,加上useParams返回引用型对象导致参数对比偏差。调整方案:
- 直接通过
params.place的存在状态判断是否禁用背景动画,简化逻辑 - 给背景页面的motion组件设置固定key,确保模态框切换时
AnimatePresence不会触发动画
问题2:根据导航标签位置动态调整过渡方向
先定义导航标签的固定顺序,通过对比前后路由参数在顺序中的索引,动态设置动画的进入/退出方向。
修改后的完整代码
import { useParams, useContext, useEffect, useState, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { LayoutRouterContext } from 'next/dist/shared/lib/router-context'; import { PropsWithChildren, ReactNode, Params } from 'react'; const FrozenRouter = (props: PropsWithChildren<{}>) => { const context = useContext(LayoutRouterContext); const frozen = useRef(context).current; return <LayoutRouterContext.Provider value={frozen}>{props.children}</LayoutRouterContext.Provider>; }; // 按导航栏标签顺序定义参数列表,根据实际配置修改 const NAV_CATEGORY_ORDER = ['restaurant', 'hotel', 'attraction', 'shopping']; const Layout = ({ children }: { children: ReactNode }) => { const params = useParams(); const paramsBeforeChange = useRef<Params>({}); const keyCounter = useRef(0); const [transitionDir, setTransitionDir] = useState<'next' | 'prev'>('next'); useEffect(() => { const { category: prevCategory, place: prevPlace } = paramsBeforeChange.current; const { category: currCategory, place: currPlace } = params; // 模态框路由(含place参数),不触发背景动画 if (prevPlace || currPlace) { paramsBeforeChange.current = {...params}; // 深拷贝避免引用问题 return; } // 判断过渡方向:对比前后参数在导航顺序中的索引 const prevIndex = NAV_CATEGORY_ORDER.indexOf(prevCategory || ''); const currIndex = NAV_CATEGORY_ORDER.indexOf(currCategory || ''); setTransitionDir(currIndex > prevIndex ? 'next' : 'prev'); // 递增key触发页面过渡动画 keyCounter.current++; paramsBeforeChange.current = {...params}; }, [params]); const isModalRoute = !!params.place; return ( <> <Navbar /> <AnimatePresence mode="popLayout" initial={false}> <motion.main key={isModalRoute ? 'modal-background' : keyCounter.current} className="h-screen overflow-y-scroll pb-8 pt-28" initial={{ opacity: 1, x: transitionDir === 'next' ? '100%' : '-100%' }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 1, x: transitionDir === 'next' ? '-100%' : '100%' }} transition={{ duration: 0.6, ease: "easeInOut" }} > <FrozenRouter>{children}</FrozenRouter> </motion.main> </AnimatePresence> </> ); };
关键修改点说明
模态框动画阻止:
- 用
isModalRoute直接判断当前路由类型,给motion组件设置固定key,避免AnimatePresence触发背景动画 - 用深拷贝
{...params}存储历史参数,解决useParams返回引用对象导致的对比失效问题
- 用
动态过渡方向:
NAV_CATEGORY_ORDER数组必须和导航栏标签的渲染顺序完全匹配- 通过前后参数的索引差判断切换方向,动态设置动画的初始/退出位置,实现对应方向的过渡效果
额外注意事项
- 若路由参数不是
category、模态框参数不是place,直接替换成你实际使用的参数名即可 - 导航栏标签顺序调整时,同步更新
NAV_CATEGORY_ORDER数组
内容的提问来源于stack exchange,提问作者Iftach
相关产品推荐
相关产品推荐

