如何使用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
相关产品推荐
相关产品推荐

