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

React拖拽时保持被拖拽元素尺寸不变的实现方案

问题:拖拽排序时元素尺寸被拉伸/收缩

我用React开发了一个支持拖拽排序句子的应用,现在遇到一个问题:小尺寸的拖拽元素经过大尺寸元素时会被拉伸,大尺寸元素经过小尺寸元素时会被收缩。需要在拖拽过程中保持被拖拽元素的原始尺寸。

附上现有代码:

App.jsx

import React, { useState, useEffect } from 'react';
import { DndContext, closestCenter,useTransform } from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  verticalListSortingStrategy

} from '@dnd-kit/sortable';
import 'bootstrap/dist/css/bootstrap.min.css';
import { SortableItem } from './SortableItem.jsx';
import './App.css';

function App() {
  const [ranking, setRanking] = useState([
    "I dont understand why people dont accept that no matter what,Boys will be boys and men will be men",
    "She is such a drama queen.",
    "Don't be a sissy.",
    "Man is a better driver than women.",
    "God is a man."
  ]);

  function handleDragEnd(event) {
    console.log('Drag');
    const { active, over } = event;
    console.log('Active:' + active.id);
    console.log('Over :' + over.id);

    
    if (active.id !== over.id) {
      setRanking((items) => {
        const activeIndex = items.indexOf(active.id);
        const overIndex = items.indexOf(over.id);
        console.log(arrayMove(items, activeIndex, overIndex));
        return arrayMove(items, activeIndex, overIndex);
      });
    }
  }


  return (
    <div>
      <DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd} >
        <h3 style={{ color: 'black', fontWeight: 'bold' }} align="center">
          Rank the following sentences from most to least sexist.
        </h3>
        <div className="frame">
          <SortableContext items={ranking} strategy={verticalListSortingStrategy}>
            {ranking.map((rank) => (
              <SortableItem key={rank} id={rank} className="sortable-item" />
            ))}
          </SortableContext>
          <div className="button" style={{ display: 'flex', justifyContent: 'center' }}>
            <button
              className="btn submit-button"
              align="center"
              onClick={(e) => {
                e.preventDefault();
                console.log(ranking);
              }}
            >
              Submit
            </button>
          </div>
        </div>
      </DndContext>
    </div>
  );
}

export default App;

SortableItem.jsx

import React from "react";
import Card from 'react-bootstrap/Card';

import { useSortable } from "@dnd-kit/sortable";
import {CSS} from "@dnd-kit/utilities";

export function SortableItem(props){
    //props.id
const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition
} = useSortable({id: props.id});

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    marginBottom: "1rem",
  };

  return (
    <div
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
      className="hover-card"
    >
      <Card body>{props.id}</Card>
    </div>
  );
}

我尝试过useTransform、useRef、设置卡片尺寸和响应式CSS,但效果都不符合预期,希望得到解决方法。


解决方案

这个问题的核心是@dnd-kit默认在拖拽时会用占位符元素替换原位置,占位符会继承目标位置的尺寸,导致拖拽元素变形。要解决这个问题,我们可以使用DragOverlay组件渲染独立的、保持原始尺寸的拖拽元素。

步骤1:修改App.jsx,添加DragOverlay

在DndContext内部引入DragOverlay,并管理当前激活的拖拽元素:

import React, { useState } from 'react';
import { DndContext, closestCenter, DragOverlay } from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  verticalListSortingStrategy
} from '@dnd-kit/sortable';
import 'bootstrap/dist/css/bootstrap.min.css';
import { SortableItem } from './SortableItem.jsx';
import './App.css';

function App() {
  const [ranking, setRanking] = useState([
    "I dont understand why people dont accept that no matter what,Boys will be boys and men will be men",
    "She is such a drama queen.",
    "Don't be a sissy.",
    "Man is a better driver than women.",
    "God is a man."
  ]);
  const [activeId, setActiveId] = useState(null);

  function handleDragStart(event) {
    setActiveId(event.active.id);
  }

  function handleDragEnd(event) {
    const { active, over } = event;
    setActiveId(null);
    
    if (active.id !== over.id) {
      setRanking((items) => {
        const activeIndex = items.indexOf(active.id);
        const overIndex = items.indexOf(over.id);
        return arrayMove(items, activeIndex, overIndex);
      });
    }
  }

  return (
    <div>
      <DndContext 
        collisionDetection={closestCenter} 
        onDragEnd={handleDragEnd}
        onDragStart={handleDragStart}
      >
        <h3 style={{ color: 'black', fontWeight: 'bold' }} align="center">
          Rank the following sentences from most to least sexist.
        </h3>
        <div className="frame">
          <SortableContext items={ranking} strategy={verticalListSortingStrategy}>
            {ranking.map((rank) => (
              <SortableItem key={rank} id={rank} className="sortable-item" />
            ))}
          </SortableContext>
          <div className="button" style={{ display: 'flex', justifyContent: 'center' }}>
            <button
              className="btn submit-button"
              onClick={(e) => {
                e.preventDefault();
                console.log(ranking);
              }}
            >
              Submit
            </button>
          </div>
        </div>
        {/* 渲染独立的拖拽元素 */}
        <DragOverlay>
          {activeId ? (
            <div className="hover-card" style={{ pointerEvents: 'none' }}>
              <Card body>{activeId}</Card>
            </div>
          ) : null}
        </DragOverlay>
      </DndContext>
    </div>
  );
}

export default App;

步骤2:修改SortableItem.jsx,隐藏拖拽中的原元素

拖拽时隐藏原元素,避免和克隆的拖拽元素重叠:

import React from "react";
import Card from 'react-bootstrap/Card';
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

export function SortableItem(props){
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging
  } = useSortable({id: props.id});

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    marginBottom: "1rem",
    opacity: isDragging ? 0 : 1,
    visibility: isDragging ? 'hidden' : 'visible'
  };

  return (
    <div
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
      className="hover-card"
    >
      <Card body>{props.id}</Card>
    </div>
  );
}

步骤3:添加CSS确保拖拽元素尺寸稳定

在App.css中添加样式,防止拖拽元素被容器拉伸:

.dndkit-drag-overlay {
  pointer-events: none;
}

.dndkit-drag-overlay .hover-card {
  width: auto;
  min-width: 200px;
  max-width: 90%;
}

原理说明

  • DragOverlay渲染的拖拽元素独立于排序列表,不会继承目标位置的尺寸,完全保持原始元素的大小。
  • 拖拽时隐藏原元素,避免视觉重叠和交互冲突。
  • CSS限制拖拽元素的宽高范围,进一步确保尺寸稳定。

内容的提问来源于stack exchange,提问作者Sitashma Rajbhandari

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 23:04:55