Nextjs+Framer Motion惯性滚动页面转场问题求助
问题描述
我在Next.js中用Framer Motion实现惯性滚动,搭配Animate Presence做页面转场动画时遇到了几个问题:
- 通过
useScroll获取滚动进度,用弹簧物理效果做Y轴变换,页面导航时调用Animate Presence的onExitComplete回调执行window.scrollTo(0,0)重置滚动位置,但弹簧变换的特性导致滚动重置无法立即完成,新页面挂载时这个变换还在运行。 - 在Next.js的Link组件里设置了
scroll={false}禁用默认滚动到顶部的功能,想通过Animate Presence手动处理,但这个设置似乎不起作用。 - 发现直接把
scrollY传入变体对象的y属性能避免转场时的异常滚动,但如果用scrollY - 128这类表达式就会失效,当前变体代码如下:
const variants = { hidden: { opacity: 0, x: 0, y: 64 }, enter: { opacity: 1, x: 0, y: 0 }, exit: { opacity: 0, x: 0, y: scrollY }, };
解决方案
1. 修复滚动重置延迟问题
别依赖onExitComplete触发滚动重置,导航一开始就同步锁定滚动状态,同时给弹簧动画加onComplete回调确保滚动重置彻底完成:
- 导航触发时(比如点击Link前),设个
isNavigating状态标记,让当前页面的弹簧动画提前终止或切换成无物理效果的过渡。 - 用
requestAnimationFrame包裹window.scrollTo,确保在DOM更新前执行:
const handleNavigation = () => { setIsNavigating(true); requestAnimationFrame(() => { window.scrollTo({ top: 0, behavior: 'instant' }); }); };
- 在Framer Motion组件上,根据
isNavigating切换动画配置,比如把弹簧换成tween:
<motion.div animate={isNavigating ? 'exit' : 'enter'} transition={isNavigating ? { duration: 0.2, ease: 'easeInOut' } : { type: 'spring', stiffness: 300 }} />
2. 让Next.js Link的scroll={false}生效
确保用的是Next.js自带的next/link,且正确嵌套子元素(比如a标签),同时检查有没有全局滚动监听逻辑覆盖这个设置:
import Link from 'next/link'; <Link href="/about" scroll={false} onClick={handleNavigation}> <a>About</a> </Link>
如果还是无效,就在_app.js里全局拦截导航事件手动控制滚动:
import { useRouter } from 'next/router'; const MyApp = ({ Component, pageProps }) => { const router = useRouter(); useEffect(() => { const handleRouteChangeStart = () => { window.scrollTo(0, 0); }; router.events.on('routeChangeStart', handleRouteChangeStart); return () => { router.events.off('routeChangeStart', handleRouteChangeStart); }; }, [router]); return <Component {...pageProps} />; };
3. 解决变体中表达式失效的问题
变体里的动态表达式不能直接写死,要改用animate/style动态传递,或者用useMotionValue计算:
- 方法一:在组件内计算动态y值,传递给
animate
const { scrollY } = useScroll(); const exitY = useTransform(scrollY, (y) => y - 128); <motion.div variants={variants} animate={{ y: isNavigating ? exitY : 0 }} />
- 方法二:把变体改成函数形式,接收动态参数
const getVariants = (scrollY) => ({ hidden: { opacity: 0, x: 0, y: 64 }, enter: { opacity: 1, x: 0, y: 0 }, exit: { opacity: 0, x: 0, y: scrollY - 128 }, }); // 使用时 const variants = getVariants(scrollY.get());
内容的提问来源于stack exchange,提问作者connorhansen
相关产品推荐
相关产品推荐

