Framer Motion布局平滑过渡问题:文本缩短时容器尺寸跳变修复求助
修复Framer Motion轮播文字容器尺寸平滑过渡问题(文本缩短时跳变)
问题描述
使用Framer Motion实现轮播文字效果时,容器在文本变长时能平滑放大,但文本缩短时会突然跳变到新尺寸,无法平滑过渡。
问题原因
当前实现中,AnimatePresence会在旧元素退出动画结束后将其从DOM中移除:
- 文本变长时,新元素宽度更大,进入时会立即撑开容器,结合
layoutprop实现平滑放大; - 文本缩短时,旧元素退出后DOM中只剩更窄的新元素,容器失去旧元素的宽度支撑,瞬间缩小导致跳变。
解决方案
方法一:手动控制新旧元素共存至过渡完成
通过维护previous状态,让旧元素在容器完成宽度过渡后再移除,确保过渡期间容器始终有足够的宽度支撑,实现平滑收缩。
修改后的代码:
import React from 'react'; import { motion } from 'framer-motion'; interface RotatingTextProps { words: string[]; } function RotatingText(props: RotatingTextProps) { const { words } = props; const [current, setCurrent] = React.useState(0); const [previous, setPrevious] = React.useState<number | null>(null); React.useEffect(() => { const interval = setInterval(() => { // 先记录当前索引为旧元素 setPrevious(current); // 更新当前索引为下一个文字 setCurrent((curr) => (curr + 1) % words.length); // 等待过渡完成后移除旧元素(与过渡时间保持一致) setTimeout(() => setPrevious(null), 500); }, 2000); return () => clearInterval(interval); }, [current, words.length]); return ( <motion.div layout transition={{ duration: 0.5 }} style={{ backgroundColor: 'yellow', display: 'inline-block', }} > <h1 style={{ display: 'inline-grid', overflow: 'hidden', }} > {/* 渲染旧元素并执行退出动画 */} {previous !== null && ( <motion.span key={previous} animate={{ transform: 'translateY(-100%)', opacity: 0 }} transition={{ duration: 0.5, ease: 'easeInOut' }} style={{ display: 'inline-block', gridColumn: 1, gridRow: 1 }} > {words[previous]} </motion.span> )} {/* 渲染新元素并执行进入动画 */} <motion.span key={current} initial={{ transform: 'translateY(100%)', opacity: 0 }} animate={{ transform: 'translateY(0%)', opacity: 1 }} transition={{ duration: 0.5, ease: 'easeInOut' }} style={{ display: 'inline-block', gridColumn: 1, gridRow: 1 }} > {words[current]} </motion.span> </h1> </motion.div> ); }
方法二:使用LayoutGroup共享布局变化
利用Framer Motion的LayoutGroup组件,让容器和轮播文字共享布局状态,自动捕捉尺寸变化并实现平滑过渡。
修改后的代码:
import React from 'react'; import { motion, AnimatePresence, LayoutGroup } from 'framer-motion'; interface RotatingTextProps { words: string[]; } function RotatingText(props: RotatingTextProps) { const { words } = props; const [current, setCurrent] = React.useState(0); React.useEffect(() => { const interval = setInterval( () => setCurrent((curr) => (curr + 1) % words.length), 2000 ); return () => clearInterval(interval); }, [words.length]); return ( <LayoutGroup> <motion.div transition={{ duration: 0.5 }} style={{ backgroundColor: 'yellow', display: 'inline-block', }} > <h1 style={{ display: 'inline-grid', overflow: 'hidden', }} > <AnimatePresence initial={false} mode="sync"> <motion.span key={current} layout // 添加layout prop,让LayoutGroup捕捉尺寸变化 initial={{ transform: 'translateY(100%)', opacity: 0 }} animate={{ transform: 'translateY(0%)', opacity: 1 }} exit={{ transform: 'translateY(-100%)', opacity: 0 }} transition={{ duration: 0.5, ease: 'easeInOut' }} style={{ display: 'inline-block', gridColumn: 1, gridRow: 1 }} > {words[current]} </motion.span> </AnimatePresence> </h1> </motion.div> </LayoutGroup> ); }
说明:mode="sync"让新旧元素的动画同时执行,配合layout prop和LayoutGroup,容器会基于新旧元素的尺寸变化自动平滑过渡宽度。
内容的提问来源于stack exchange,提问作者Andrius
相关产品推荐
相关产品推荐

