路由切换时动画div向上偏移问题及X轴动画优化方案咨询
解决粘性导航栏Framer Motion下划线Y轴动画问题
问题描述
我用framer-motion实现水平导航栏按钮点击时的下划线动画,<ul>设置为position: sticky定位,代码如下:
<ul className="sticky"> <li className="relative text-base font-normal text-gray-400"> {activePath === '/' ? ( <motion.div className="absolute left-0 top-0 right-0 h-full w-full items-center gap-x-2 rounded-md" layoutId="underline" key={activePath} /> ) : null} <Link href="/" aria-label="Home" type="button"> Home </Link> </li> <li className="relative text-base font-normal text-gray-400"> {activePath === '/about' ? ( <motion.div className="absolute left-0 top-0 right-0 h-full w-full items-center gap-x-2 rounded-md" layoutId="underline" key={activePath} /> ) : null} <Link href="/about" aria-label="About" type="button"> About </Link> </li> </ul>
页面在顶部时动画正常,但滚动后点击Link切换路由,scrollTop变化会导致动画div向上移动,能看到它从底部动画回到顶部位置。推测是<ul>元素跳转到页面顶部,而动画过渡延迟导致的。我希望只保留X轴动画,去掉Y轴动画,但动画似乎跟不上<ul>的位置更新。求无需强制整个组件更新的解决方法。
解决方案
方法1:通过transition禁用Y轴动画
给motion.div添加layout="position"属性,并在transition中明确禁用Y轴动画,仅保留X轴的过渡效果:
<motion.div className="absolute left-0 top-0 right-0 h-full w-full items-center gap-x-2 rounded-md" layoutId="underline" key={activePath} layout="position" transition={{ x: { type: "spring", stiffness: 500, damping: 30 }, y: false // 直接关闭Y轴动画 }} />
layout="position"让Framer Motion仅处理元素的位置变化,配合y: false可以彻底消除Y轴的过渡动画,下划线只会在X轴上平滑切换。
方法2:将动画元素脱离粘性容器布局流
把下划线元素从<li>中抽离,放到与<ul>同级的容器中,通过绝对定位匹配导航项的位置。这样下划线不受粘性容器的位置变化影响,仅根据activePath更新X轴坐标:
<div className="relative"> <ul className="sticky"> <li className="relative text-base font-normal text-gray-400"> <Link href="/" aria-label="Home" type="button"> Home </Link> </li> <li className="relative text-base font-normal text-gray-400"> <Link href="/about" aria-label="About" type="button"> About </Link> </li> </ul> {/* 下划线动画元素独立放置 */} <motion.div className="absolute h-full rounded-md bg-blue-500" layoutId="underline" transition={{ x: { type: "spring", stiffness: 500, damping: 30 } }} style={{ left: activePath === '/' ? '0' : '8rem', // 根据实际导航项宽度调整 width: activePath === '/' ? '5rem' : '6rem' // 匹配对应导航项宽度 }} /> </div>
这种方式从根源上避免了粘性容器位置变化对动画元素的影响,仅保留X轴的切换动画。
方法3:用AnimatePresence锁定Y轴位置
用AnimatePresence包裹动画元素,强制initial、animate、exit状态的Y轴值为0,让动画仅在X轴生效:
<li className="relative text-base font-normal text-gray-400"> <AnimatePresence> {activePath === '/' && ( <motion.div className="absolute left-0 top-0 right-0 h-full w-full items-center gap-x-2 rounded-md" layoutId="underline" initial={{ y: 0 }} animate={{ y: 0 }} exit={{ y: 0 }} transition={{ x: { type: "spring", stiffness: 500, damping: 30 } }} /> )} </AnimatePresence> <Link href="/" aria-label="Home" type="button"> Home </Link> </li>
通过固定Y轴的初始、动画、退出状态,忽略容器位置变化带来的Y轴偏移,只保留X轴的平滑过渡。
内容的提问来源于stack exchange,提问作者James Ives
相关产品推荐
相关产品推荐

