React列表元素重排时如何实现垂直位置的平滑过渡动画
问题根因
你当前的代码中,用唯一id作为key的元素复用逻辑、transform位移计算、transition过渡属性的配置都是正确的。出现向下移动元素无过渡动画的核心问题有两个:
- 承载列表的相对定位父容器没有设置固定高度,子元素全部为绝对定位会导致父容器高度塌陷为0,浏览器布局计算异常,无法正确捕获元素位移的初始状态
- React批量更新状态时,部分元素的旧位移值还没被浏览器渲染帧记录,就被替换为新的位移值,导致过渡起点丢失,看起来就像直接跳转到目标位置。
修复方案(无第三方依赖)
只需要做少量调整即可实现所有元素上下移动都有平滑过渡:
- 给父容器设置和列表总高度一致的固定值,避免布局塌陷
- 可选:更新状态前强制触发一次浏览器布局计算,保证旧位移值被正确记录
修正后的完整代码如下:
import shuffle from "lodash.shuffle"; import { useState } from "react"; const data = [ { id: 1, name: "Foo" }, { id: 2, name: "Bar" }, { id: 3, name: "Baz" } ]; // 抽离常量方便统一维护 const ITEM_HEIGHT = 42; export default function App() { const [items, setItems] = useState(data); return ( <div> <button onClick={() => { // 强制触发浏览器布局计算,确保旧状态被记录 document.body.offsetHeight; setItems(shuffle(data)); }} > Shuffle </button> {/* 父容器设置固定高度,避免布局塌陷 */} <div style={{ position: "relative", height: ITEM_HEIGHT * data.length }}> {items.map(({ id, name }, index) => ( <div key={id} style={{ position: "absolute", border: "1px solid #ddd", width: "100%", height: ITEM_HEIGHT, boxSizing: "border-box", transition: "transform 1s ease-in-out", transform: `translate(0px, ${index * ITEM_HEIGHT}px)` }} > {name} </div> ))} </div> </div> ); }
如果你的实际场景中列表元素数量是动态变化的,只需要把父容器的高度改为ITEM_HEIGHT * items.length即可适配。
内容的提问来源于stack exchange,提问作者Sam Ferrell
相关产品推荐
相关产品推荐

