React Spring:如何在父组件过渡完成后触发子组件动画链式调用?
如何让组件内部动画在页面过渡动画完成后执行?
这个场景我太熟悉了——当页面级过渡和组件内部动画撞在一起,又没法直接用useChain(毕竟不在同一个组件树层级),确实头疼。给你几个经过实践验证的解决方案,按你的技术栈选就行:
方案1:状态传递 + 动画完成回调(最通用)
核心思路是在页面过渡的根组件里监听动画完成事件,把“过渡已完成”的状态传递给子组件,子组件只在这个状态为true时触发内部动画。
示例代码:
首先在App层维护状态并传递:
import { useState } from 'react'; import { motion } from 'framer-motion'; import { Switch, Route } from 'react-router-dom'; import LandingPage from './LandingPage'; function App() { // 标记页面过渡是否完成 const [isPageTransitionDone, setIsPageTransitionDone] = useState(false); return ( // 页面过渡容器 <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} // 动画完成时更新状态 onComplete={() => setIsPageTransitionDone(true)} // 路由切换时重置状态,避免下次进入直接触发内部动画 key={window.location.pathname} > <Switch> <Route path="/landing" render={() => <LandingPage isPageReady={isPageTransitionDone} />} /> {/* 其他路由组件 */} </Switch> </motion.div> ); }
然后在LandingPage里根据状态触发内部动画:
import { useEffect } from 'react'; import { motion, useAnimation } from 'framer-motion'; function LandingPage({ isPageReady }) { const internalControls = useAnimation(); useEffect(() => { // 只有页面过渡完成后,才启动内部多阶段动画 if (isPageReady) { internalControls.start([ { opacity: 0, scale: 0.8 }, { opacity: 1, scale: 1 }, { rotate: 5 }, { rotate: 0 } // 更多动画阶段... ]); } }, [isPageReady, internalControls]); return ( <motion.div animate={internalControls} initial={{ opacity: 0 }}> {/* 你的组件内容 */} <h1>欢迎来到首页</h1> <p>这是内部动画演示</p> </motion.div> ); }
方案2:用Context共享动画状态(适合多组件需要同步的场景)
如果多个页面组件都需要等待页面过渡完成,用Context把状态全局共享会更方便,不用一个个传props。
示例代码:
先创建动画上下文:
// AnimationContext.js import { createContext, useContext } from 'react'; const AnimationContext = createContext({ isPageTransitionDone: false, }); export const useAnimationContext = () => useContext(AnimationContext); export default AnimationContext;
然后在App层提供上下文:
import { useState, useEffect } from 'react'; import { motion, useAnimation } from 'framer-motion'; import { Switch, Route, useLocation } from 'react-router-dom'; import AnimationContext from './AnimationContext'; import LandingPage from './LandingPage'; function App() { const location = useLocation(); const pageControls = useAnimation(); const [isPageTransitionDone, setIsPageTransitionDone] = useState(false); // 监听页面过渡动画的完成事件 useEffect(() => { const unsubscribe = pageControls.on('animationComplete', () => { setIsPageTransitionDone(true); }); // 路由切换时重置状态 setIsPageTransitionDone(false); // 启动页面过渡动画 pageControls.start({ opacity: 1, y: 0 }); return unsubscribe; }, [location.pathname, pageControls]); return ( <AnimationContext.Provider value={{ isPageTransitionDone }}> <motion.div initial={{ opacity: 0, y: 20 }} animate={pageControls} exit={{ opacity: 0, y: -20 }} > <Switch location={location} key={location.pathname}> <Route path="/landing" component={LandingPage} /> </Switch> </motion.div> </AnimationContext.Provider> ); }
最后在LandingPage里消费上下文:
import { useEffect } from 'react'; import { motion, useAnimation } from 'framer-motion'; import { useAnimationContext } from './AnimationContext'; function LandingPage() { const { isPageTransitionDone } = useAnimationContext(); const internalControls = useAnimation(); useEffect(() => { if (isPageTransitionDone) { internalControls.start({/* 你的内部动画序列 */}); } }, [isPageTransitionDone, internalControls]); return <motion.div animate={internalControls}>...</motion.div>; }
关键注意点
- 记得路由切换时重置状态:不然用户从其他页面回到LandingPage时,
isPageTransitionDone还是true,内部动画不会重新触发。 - 如果用
AnimatePresence处理路由切换的退出+进入动画,建议监听onExitComplete事件(旧页面退出完成后)再触发新页面的内部动画,体验更流畅。
内容的提问来源于stack exchange,提问作者Z.M
相关产品推荐
相关产品推荐

