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

React列表拖拽项动画优化技术求助

优化React拖拽列表的顺滑动画效果

问题背景

已实现包含10个项的React拖拽排序列表,基础抓取动画已完成,但拖拽过程中列表项移动卡顿,希望实现:

  • 拖拽项顺滑上下移动
  • 被拖拽覆盖的项向相反方向顺滑移动
  • 避免项重叠导致的事件触发错误

优化后可运行代码

JavaScript 部分

const App = () => {
  const [myArray, setMyArray] = React.useState([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
  const [draggedIndex, setDraggedIndex] = React.useState(-1);
  const [dragDirection, setDragDirection] = React.useState(null); // 记录拖拽方向:up/down

  const onDragStart = (e, index) => {
    setDraggedIndex(index);
    const emptyDiv = document.createElement('div');
    emptyDiv.style.width = '0px';
    emptyDiv.style.height = '0px';
    e.dataTransfer.setDragImage(emptyDiv, 0, 0);
  };

  const onDragOver = (e, index) => {
    e.preventDefault();
    if (draggedIndex === -1 || draggedIndex === index) return;

    // 判断拖拽方向
    const direction = index < draggedIndex ? 'up' : 'down';
    setDragDirection(direction);

    // 更新数组顺序
    const newArray = [...myArray];
    const [draggedItem] = newArray.splice(draggedIndex, 1);
    newArray.splice(index, 0, draggedItem);
    setMyArray(newArray);
    setDraggedIndex(index);
  };

  const onDragEnd = (e) => {
    setDraggedIndex(-1);
    setDragDirection(null);
  };

  return (
    <div className="App">
      {myArray.map((x, i) => {
        let itemClass = 'listItem';
        // 给拖拽项添加方向类,给被移动的项添加反向类
        if (i === draggedIndex) {
          itemClass += ` draggable dragged-${dragDirection}`;
        } else if (dragDirection === 'up' && i === draggedIndex + 1) {
          itemClass += ' listItem-down';
        } else if (dragDirection === 'down' && i === draggedIndex - 1) {
          itemClass += ' listItem-up';
        }

        return (
          <div
            className={itemClass}
            draggable
            key={x}
            onDragStart={(e) => onDragStart(e, i)}
            onDragOver={(e) => onDragOver(e, i)}
            onDragEnd={onDragEnd}
          >
            <h3>hello - {x}</h3>
          </div>
        );
      })}
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

CSS 部分

.App {
  text-align: center;
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: 5px; /* 用gap替代margin,避免margin重叠问题 */
}

.listItem {
  border: 2px solid black;
  width: 400px;
  padding: 10px;
  cursor: grab;
  transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
  position: relative;
  z-index: 1;
}

.draggable {
  border-color: green;
  transform: scale(1.08);
  cursor: grabbing;
  z-index: 10; /* 拖拽项置于顶层,避免被其他项遮挡 */
}

/* 拖拽项向上移动的动画 */
.dragged-up {
  transform: scale(1.08) translateY(-100%);
}

/* 拖拽项向下移动的动画 */
.dragged-down {
  transform: scale(1.08) translateY(100%);
}

/* 其他项向下移动的动画 */
.listItem-down {
  transform: translateY(100%);
  z-index: 0;
}

/* 其他项向上移动的动画 */
.listItem-up {
  transform: translateY(-100%);
  z-index: 0;
}

HTML 部分

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

关键改动说明

  • 状态管理:新增dragDirection状态记录拖拽方向(up/down),用于给对应项添加动画类
  • 项类名动态控制:根据当前索引和拖拽方向,给拖拽项和被移动的相邻项添加对应的动画类
  • CSS优化:
    • 用gap替代margin避免项间距重叠问题
    • 给拖拽项设置更高的z-index,确保其在顶层,防止事件被遮挡
    • 使用cubic-bezier过渡曲线让动画更自然
    • 为不同方向的移动定义对应的translateY变换,实现顺滑的位置过渡
  • 数组更新逻辑:改用slice和splice的组合,更清晰地处理拖拽项的位置调整

内容的提问来源于stack exchange,提问作者Sebastian Meckovski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 10:55:21