Next.js中Framer Motion导航组件仅首次进入/app时触发动画的问题
解决Next.js Layout中framer-motion导航组件重复动画问题
问题核心:每次在/app下切换路由(比如从/app到/app/chats),导航组件的入场动画都会重复触发,需要仅在首次进入/app时播放一次动画。
原因
framer-motion的initial+animate组合默认会在组件每次挂载或重新渲染时触发动画。由于Next.js路由切换时,Layout组件不会卸载,但导航组件可能因路由变化导致的状态更新(比如isChatsPage的变化)重新渲染,从而再次触发动画逻辑。
解决方案:用useRef标记动画是否已执行
通过useRef记录动画是否已经播放过,动态控制framer-motion的初始状态,确保仅首次挂载时播放动画。
修改UserNavigation组件代码
import { useRef, useEffect } from 'react'; import { motion } from 'framer-motion'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { ChatBubbleBottomCenterTextIcon } from '@heroicons/react/24/outline'; import classNames from 'classnames'; export const UserNavigation = () => { // 标记动画是否已执行过 const hasAnimated = useRef(false); const pathname = usePathname(); const isChatsPage = pathname === '/app/chats'; // 组件挂载后标记动画已执行 useEffect(() => { hasAnimated.current = true; }, []); return ( <motion.section // 首次挂载时用初始偏移,之后直接用最终状态 initial={hasAnimated.current ? { x: 0 } : { x: -56 }} animate={{ x: 0 }} transition={{ bounce: false }} className="h-[100%] w-14 bg-gray-900" > <div className="flex flex-col gap-4 items-center justify-center mt-2"> <Link href="/app/chats"> <button className={classNames( isChatsPage ? "bg-gray-700" : "hover:bg-gray-700", "text-white rounded-lg p-2 transition duration-200" )} > <ChatBubbleBottomCenterTextIcon className="w-6 h-6" /> <span className="sr-only">Chats</span> </button> </Link> </div> </motion.section> ); };
原理说明
hasAnimated是一个不会触发组件重新渲染的ref变量,用于记录动画是否已经执行。- 组件首次挂载时,
hasAnimated.current为false,initial设置为{x: -56},配合animate={x:0}触发入场动画。 useEffect在组件挂载完成后将hasAnimated.current设为true,后续路由切换导致组件重新渲染时,initial直接设为最终状态{x:0},和animate状态一致,不会再次触发动画。
如果需要更精细的控制(比如离开/app再回来时也不播放动画),可以将状态持久化到localStorage:
useEffect(() => { const animated = localStorage.getItem('navAnimated'); if (!animated) { hasAnimated.current = false; localStorage.setItem('navAnimated', 'true'); } else { hasAnimated.current = true; } }, []);
内容的提问来源于stack exchange,提问作者etztrefis
相关产品推荐
相关产品推荐

