使用React Transition Group实现列表项增删时高度平滑动画
React Transition Group 列表增删时容器高度平滑过渡实现方案
核心问题本质是CSS原生不支持height: auto到固定像素值的自动过渡,直接增删节点时浏览器会瞬间重排到内容固有高度,就会出现跳变。下面两种方案都能解决,第一种不需要额外引入依赖,完全适配现有React Transition Group的代码逻辑。
方案1:基于Transition生命周期手动控制容器高度(无额外依赖)
核心思路是在项入场、离场的动画节点,通过JS实时计算容器的真实内容高度,给容器设置可过渡的固定height值,动画结束后再还原为auto适配后续内容变化。
- 实现步骤:
- 给列表外层容器绑定ref,添加
height属性的CSS过渡,设置overflow: hidden避免内容溢出穿帮 - 监听
CSSTransition的onEnter、onExit回调,每次项开始入场/离场时,先拿到容器当前的真实滚动高度,再动态设置容器的height值触发过渡 - 动画结束后(监听
onEntered、onExited回调)把容器height重置为auto,适配窗口缩放、内容动态变更的场景
- 给列表外层容器绑定ref,添加
代码示例:
import { useRef, useState } from 'react'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; function AnimateList() { const [list, setList] = useState([]); const containerRef = useRef(null); // 计算并更新容器真实高度,触发过渡 const updateContainerHeight = () => { const el = containerRef.current; if (!el) return; // 临时切到auto拿真实内容高度 const prevHeight = el.style.height; el.style.height = 'auto'; const targetHeight = `${el.scrollHeight}px`; // 还原之前的高度,强制重排后设置目标高度,保证过渡触发 el.style.height = prevHeight; el.offsetHeight; // 读取offset属性强制浏览器重排,不能省 el.style.height = targetHeight; }; // 动画结束后重置高度为auto const resetHeight = () => { if (containerRef.current) containerRef.current.style.height = 'auto'; }; return ( <div ref={containerRef} style={{ transition: 'height 300ms ease-in-out', overflow: 'hidden' }} > <TransitionGroup> {list.map(item => ( <CSSTransition key={item.id} timeout={300} classNames="list-item" onEnter={updateContainerHeight} onEntered={resetHeight} onExit={updateContainerHeight} onExited={resetHeight} > <div className="list-item-content">{item.text}</div> </CSSTransition> ))} </TransitionGroup> </div> ); }
配套的列表项动画CSS:
.list-item-enter { opacity: 0; transform: translateY(-8px); } .list-item-enter-active { opacity: 1; transform: translateY(0); transition: all 300ms ease-in-out; } .list-item-exit { opacity: 1; } .list-item-exit-active { opacity: 0; transform: translateY(-8px); transition: all 300ms ease-in-out; }
方案2:配合现成折叠动画组件实现(少写DOM操作逻辑)
如果不想手动处理高度计算、重排触发这些细节,可以直接用成熟的折叠动画组件和React Transition Group搭配,组件内部会自动处理高度计算逻辑。
用法示例:
// 安装对应依赖后直接引入 import { Collapse } from 'react-collapse'; // 在CSSTransition内部包裹一层Collapse即可 <CSSTransition timeout={300} key={item.id}> <Collapse isOpened={true}> <div className="list-item-content">{item.text}</div> </Collapse> </CSSTransition>
这种写法不需要手动给外层容器加高度控制逻辑,每一项的展开收起动画会自动撑出容器的平滑高度过渡。
常见踩坑
- 不要尝试直接用CSS写
height: auto的transition,所有现代浏览器都不支持auto值和长度值之间的插值过渡,必须通过JS桥接固定高度值才能生效 - 列表项尽量不要用外边距(margin)做间距,margin折叠会导致
scrollHeight计算偏差,用内边距(padding)代替更稳妥 - 如果列表项内有图片、异步加载的内容,要等资源加载完成后再调用一次高度更新方法,否则会出现高度计算不足导致的二次跳变
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

