You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js14+Framer Motion路由页面过渡问题求助

问题解决方案

问题1:Parallel Routes模态框打开/关闭时背景页面过渡动画重叠

核心问题是模态框路由切换时,背景页面的动画逻辑判断不准确,加上useParams返回引用型对象导致参数对比偏差。调整方案:

  1. 直接通过params.place的存在状态判断是否禁用背景动画,简化逻辑
  2. 给背景页面的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>
    </>
  );
};

关键修改点说明

  1. 模态框动画阻止:

    • 用isModalRoute直接判断当前路由类型,给motion组件设置固定key,避免AnimatePresence触发背景动画
    • 用深拷贝{...params}存储历史参数,解决useParams返回引用对象导致的对比失效问题
  2. 动态过渡方向:

    • NAV_CATEGORY_ORDER数组必须和导航栏标签的渲染顺序完全匹配
    • 通过前后参数的索引差判断切换方向,动态设置动画的初始/退出位置,实现对应方向的过渡效果

额外注意事项

  • 若路由参数不是category、模态框参数不是place,直接替换成你实际使用的参数名即可
  • 导航栏标签顺序调整时,同步更新NAV_CATEGORY_ORDER数组

内容的提问来源于stack exchange,提问作者Iftach

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 00:43:11