You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React列表元素重排时如何实现垂直位置的平滑过渡动画

问题根因

你当前的代码中,用唯一id作为key的元素复用逻辑、transform位移计算、transition过渡属性的配置都是正确的。出现向下移动元素无过渡动画的核心问题有两个:

  1. 承载列表的相对定位父容器没有设置固定高度,子元素全部为绝对定位会导致父容器高度塌陷为0,浏览器布局计算异常,无法正确捕获元素位移的初始状态
  2. React批量更新状态时,部分元素的旧位移值还没被浏览器渲染帧记录,就被替换为新的位移值,导致过渡起点丢失,看起来就像直接跳转到目标位置。
修复方案(无第三方依赖)

只需要做少量调整即可实现所有元素上下移动都有平滑过渡:

  1. 给父容器设置和列表总高度一致的固定值,避免布局塌陷
  2. 可选:更新状态前强制触发一次浏览器布局计算,保证旧位移值被正确记录

修正后的完整代码如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 22:45:02