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

如何使用dnd-kit实现自由拖拽功能?

解决@dnd-kit自由拖拽便签后位置无法保存的问题

要实现自由拖拽并持久化位置,核心是在handleDragEnd中计算并更新便签的最终位置,具体步骤如下:

1. 维护便签的位置状态

给每个便签添加x、y坐标属性,用状态管理所有便签数据:

import { useState } from 'react';

const [notes, setNotes] = useState([
  { id: '1', content: '待办事项1', x: 50, y: 50 },
  { id: '2', content: '待办事项2', x: 150, y: 150 },
]);

2. 渲染可拖拽便签

使用Draggable组件,通过状态中的x、y设置绝对定位,同时结合transform实现拖拽时的实时位移:

import { Draggable } from '@dnd-kit/core';

{notes.map(note => (
  <Draggable key={note.id} id={note.id}>
    {(attributes, listeners, setNodeRef, transform) => (
      <div
        ref={setNodeRef}
        style={{
          position: 'absolute',
          left: note.x,
          top: note.y,
          width: '200px',
          height: '180px',
          padding: '1rem',
          backgroundColor: '#fff9c4',
          borderRadius: '8px',
          boxShadow: '0 2px 6px rgba(0,0,0,0.1)',
          ...transform // 拖拽过程中实时更新位置
        }}
        {...attributes}
        {...listeners}
      >
        {note.content}
      </div>
    )}
  </Draggable>
))}

3. 在handleDragEnd中更新最终位置

通过拖拽结束事件的delta(拖拽位移),结合便签原位置计算新坐标,更新状态:

const handleDragEnd = (event) => {
  const { active, delta } = event;
  const targetId = active.id;

  setNotes(prev => 
    prev.map(note => {
      if (note.id === targetId) {
        return {
          ...note,
          x: note.x + delta.x,
          y: note.y + delta.y
        };
      }
      return note;
    })
  );
};

4. 配置DndContext

将handleDragEnd传入DndContext,同时配置传感器和碰撞检测:

import { DndContext, DragOverlay, useSensor, useSensors, PointerSensor } from '@dnd-kit/core';
import { closestCenter } from '@dnd-kit/utilities';

const sensors = useSensors(
  useSensor(PointerSensor, {
    activationConstraint: {
      distance: 8 // 拖拽触发的最小距离,避免误触
    }
  })
);

// 在组件中渲染
<DndContext 
  sensors={sensors} 
  collisionDetection={closestCenter} 
  onDragEnd={handleDragEnd}
>
  {/* 拖拽浮层(可选,优化视觉体验) */}
  <DragOverlay>
    {activeId && (
      <div style={{
        width: '200px',
        height: '180px',
        padding: '1rem',
        backgroundColor: '#fff9c4',
        borderRadius: '8px',
        boxShadow: '0 4px 12px rgba(0,0,0,0.2)'
      }}>
        {notes.find(n => n.id === activeId)?.content}
      </div>
    )}
  </DragOverlay>
  
  {/* 渲染所有便签 */}
  {notes.map(note => (
    // 此处为步骤2的Draggable代码
  ))}
</DndContext>

关键说明

  • delta.x/delta.y是拖拽过程中鼠标的位移量,直接加到原坐标上即可得到最终位置
  • transform属性负责拖拽过程中的实时位置反馈,结束后通过状态固定位置
  • 若需要限制拖拽范围(比如不能拖出视口),可在更新坐标时添加边界判断

内容的提问来源于stack exchange,提问作者Pablo Darde

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:10:50