Next.js结合Framer Motion用Router.push做页面切换动画二次点击失效问题
问题根因
你遇到的是Framer Motion共享布局动画在Next.js路由来回切换后的状态残留问题,核心原因有三个:
- 点击图片设置的
isClicked状态在路由跳转后没有重置,返回首页时状态上下文冲突导致动画无法触发 - 列表渲染用
index作为key,可能导致元素复用异常,破坏layoutId的对应关系 - 旧版本Framer Motion的
AnimateSharedLayout没有随路由切换重置上下文,残留了之前的布局动画标识
修复方案
1. 跳转完成后重置点击状态
修改点击处理函数,利用router.push返回的Promise,等路由跳转完成后重置isClicked,避免状态残留:
const onHandlerClick = async (item, href, e) => { e.preventDefault(); setIsClicked(item); await router.push(href, { scroll: false }); // 路由跳转完成后重置状态,保证返回首页时状态干净 setIsClicked(null); };
2. 替换列表渲染的key
将遍历的key从数组下标替换为item本身,避免元素复用异常:
{images.map((item) => { return ( <motion.div key={item} // 替换原有的index className="c-home-overflowimage c-home-overflowimage2" layoutId={`imageAnimation${item}`} variants={isClicked === item ? false : postVariants} transition={{ ...transition }} > {/* 其余内部代码不变 */} </motion.div> ) })}
3. 重置AnimateSharedLayout上下文(可选,针对旧版本Framer Motion)
给AnimateSharedLayout添加和页面组件一致的key,每次路由切换强制重置共享布局上下文:
<AnimateSharedLayout type="crossfade" key={router.asPath}> <AnimatePresence exitBeforeEnter initial={false}> <Component {...pageProps} key={router.asPath} /> </AnimatePresence> </AnimateSharedLayout>
如果使用的是Framer Motion v5及以上版本,AnimateSharedLayout已经被废弃,直接移除外层的AnimateSharedLayout包裹,仅保留AnimatePresence即可正常使用layoutId共享动画。
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

