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

使用菜单后Framer Motion页面无法加载的问题求助

React + Framer Motion 页面过渡动画异常问题

我在React项目中使用framer-motion实现页面间过渡动画时遇到异常:在主页滚动一段距离后,点击菜单跳转至其他页面,新页面显示为空。通过开发者工具检查发现,之前的主页仍保留在DOM中(新页面已加载),且主页未执行退出动画。


相关代码

Home组件

import React, {useState, useEffect, useContext} from 'react'
import FirstSection from './FirstSection/FirstSection';
import StickyNav from './FirstSection/StickyNav/StickyNav';
import SndSection from "../sections/Content2/SndSection.js"
import './Home.css';
import LeftOne from "../sections/FirstSection/LeftOne.js";
import ThirdSection from './Content3/ThirdSection';
import { slideContext } from '../../App';
import { motion } from 'framer-motion';

export default function Home() {
  const context = useContext(slideContext);
    const [scroll, setScroll] = useState();
    useEffect(() => {
        const handleScroll = event => {
          setScroll(window.scrollY);
        };
    
        window.addEventListener('scroll', handleScroll);
    
        return () => {
          window.removeEventListener('scroll', handleScroll);
        };
      }, []);
  return (
    <motion.div 
    id={context.end ? "end-transition" : ""} 
    className='home'
    initial={{width:0, opacity:0}}
    animate={{width:'100%', opacity:1}}
    exit={{x:window.innerWidth, transition:{duration: 0.3}, opacity:0}}
    >
        {scroll >= 100 ? <StickyNav /> : ""}
        <div id="page">
          <LeftOne element='v1'/>
          <div id="ctn">
            <FirstSection />
            <SndSection />
            <ThirdSection />
          </div>
        </div>
    </motion.div>
  )
}

App.js

return (
    <HashRouter>
    <slideContext.Provider value={{end, setEnd, endInscr, setEndInscr, number, setNumber}}>
      <div className="App">
        <AnimatedRoutes />
      </div>
    </slideContext.Provider>
    </HashRouter>
  );

AnimatedRoutes组件

import React from 'react'
import {Routes, Route, useLocation} from "react-router-dom";
import AboutUs from './AboutUs/AboutUs';
import Error from './Error';
import Inscription from './Inscription/Inscription';
import Thanks from './Inscription/Thanks';
import Privacy from './Privacy/Privacy';
import Home from './sections/Home';
import Terms from './Terms&Conditions/Terms';

import {AnimatePresence} from 'framer-motion';
export default function AnimatedRoutes() {
    const location = useLocation();
  return (
    <AnimatePresence>
        <Routes location={location} key={location.pathname}>
            <Route path='/' element={<Home />} />
            <Route path='/register' element = {<Inscription />} />
            <Route path='/thanks-registration' element ={<Thanks />} />
            <Route path='/aboutUs' element={<AboutUs />}/>
            <Route path='/termsAndConditions' element={<Terms />} />
            <Route path="/privacy" element={<Privacy />}/>
            <Route path="/error" element={<Error />} />
        </Routes>
    </AnimatePresence>
  )
}

问题原因及修复方案

1. 核心问题分析

  • 页面滚动后,旧页面的滚动位置未重置,导致framer-motion的退出动画被阻塞,旧组件无法被正确移除
  • AnimatePresence未设置mode="wait",导致新页面提前渲染,旧页面还未完成退出动画就被叠加,视觉上显示为空
  • Home组件的scroll状态初始化未赋值,且空字符串渲染可能产生无效节点,干扰动画判断

2. 具体修复代码

修改AnimatedRoutes组件

import React from 'react'
import {Routes, Route, useLocation} from "react-router-dom";
import AboutUs from './AboutUs/AboutUs';
import Error from './Error';
import Inscription from './Inscription/Inscription';
import Thanks from './Inscription/Thanks';
import Privacy from './Privacy/Privacy';
import Home from './sections/Home';
import Terms from './Terms&Conditions/Terms';

import {AnimatePresence} from 'framer-motion';

export default function AnimatedRoutes() {
    const location = useLocation();

    // 路由切换时重置滚动位置到顶部
    React.useEffect(() => {
        window.scrollTo(0, 0);
    }, [location.pathname]);

    return (
        // 设置mode="wait",等待退出动画完成后再渲染新组件
        <AnimatePresence mode="wait">
            <Routes location={location} key={location.pathname}>
                <Route path='/' element={<Home />} />
                <Route path='/register' element = {<Inscription />} />
                <Route path='/thanks-registration' element ={<Thanks />} />
                <Route path='/aboutUs' element={<AboutUs />}/>
                <Route path='/termsAndConditions' element={<Terms />} />
                <Route path="/privacy" element={<Privacy />}/>
                <Route path="/error" element={<Error />} />
            </Routes>
        </AnimatePresence>
    )
}

优化Home组件

import React, {useState, useEffect, useContext} from 'react'
import FirstSection from './FirstSection/FirstSection';
import StickyNav from './FirstSection/StickyNav/StickyNav';
import SndSection from "../sections/Content2/SndSection.js"
import './Home.css';
import LeftOne from "../sections/FirstSection/LeftOne.js";
import ThirdSection from './Content3/ThirdSection';
import { slideContext } from '../../App';
import { motion } from 'framer-motion';

export default function Home() {
  const context = useContext(slideContext);
    // 初始化scroll为0,避免undefined判断问题
    const [scroll, setScroll] = useState(0);
    useEffect(() => {
        const handleScroll = event => {
          setScroll(window.scrollY);
        };
    
        window.addEventListener('scroll', handleScroll);
    
        return () => {
          window.removeEventListener('scroll', handleScroll);
        };
      }, []);

  return (
    <motion.div 
    id={context.end ? "end-transition" : ""} 
    className='home'
    initial={{width:0, opacity:0}}
    animate={{width:'100%', opacity:1}}
    exit={{x: window.innerWidth, opacity:0, transition:{duration: 0.3}}}
    // 添加绝对定位确保动画过程中不干扰布局
    style={{position: "absolute", top: 0, left: 0, width: "100%"}}
    >
        // 用null代替空字符串,避免渲染无效文本节点
        {scroll >= 100 ? <StickyNav /> : null}
        <div id="page">
          <LeftOne element='v1'/>
          <div id="ctn">
            <FirstSection />
            <SndSection />
            <ThirdSection />
          </div>
        </div>
    </motion.div>
  )
}

3. 额外检查点

  • 确保所有路由页面组件都用motion.div包裹并设置exit动画,否则AnimatePresence无法处理过渡
  • 检查slideContext中的end状态是否在导航跳转时被正确触发,比如在菜单点击事件中调用setEnd(true)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 10:41:17