如何在未知初始x、y坐标下使用Dnd Kit创建可拖拽卡片?
用Dnd Kit实现自由拖拽组件(无需手动设置初始位置)
核心思路
先让组件按正常文档流渲染,挂载后计算其相对于父容器的初始位置,再切换为absolute定位,后续通过Dnd Kit的拖拽状态更新位置,同时解决滚动导致的位置偏差问题。
具体实现方案
步骤1:准备父容器与拖拽组件的Ref
给父容器设置position: relative(作为绝对定位的参考),同时给父容器和拖拽组件分别绑定Ref,用于获取布局信息。
步骤2:计算初始相对位置
组件挂载后,通过getBoundingClientRect()获取元素和父容器的视口位置,结合父容器的滚动偏移量,计算出元素相对于父容器的初始left/top值,避免滚动导致的位置误差。
步骤3:结合Dnd Kit的拖拽能力
用useDraggable获取拖拽相关的属性和变换值,将初始位置与拖拽变换值结合到组件样式中,实现自由拖拽。
完整代码示例
import { useState, useEffect, useRef } from 'react'; import { useDraggable, DndContext } from '@dnd-kit/core'; import { CSS } from '@dnd-kit/utilities'; const DraggableBox = ({ id, children }) => { const containerRef = useRef(null); const boxRef = useRef(null); const [initialPos, setInitialPos] = useState({ x: 0, y: 0 }); const { attributes, listeners, setNodeRef, transform } = useDraggable({ id }); // 组件样式:初始位置 + 拖拽实时变换 const boxStyle = { width: '200px', height: '120px', background: '#e5e7eb', border: '1px solid #9ca3af', padding: '16px', position: initialPos.x === 0 && initialPos.y === 0 ? 'static' : 'absolute', left: initialPos.x, top: initialPos.y, transform: CSS.Transform.toString(transform), cursor: 'grab', }; useEffect(() => { if (!boxRef.current || !containerRef.current) return; const containerRect = containerRef.current.getBoundingClientRect(); const boxRect = boxRef.current.getBoundingClientRect(); // 计算相对父容器的初始位置,抵消滚动影响 const relativeX = boxRect.left - containerRect.left + containerRef.current.scrollLeft; const relativeY = boxRect.top - containerRect.top + containerRef.current.scrollTop; setInitialPos({ x: relativeX, y: relativeY }); }, []); return ( <div ref={(node) => { boxRef.current = node; setNodeRef(node); }} style={boxStyle} {...attributes} {...listeners} > {children} </div> ); }; export default function App() { return ( <DndContext> <div ref={containerRef} style={{ position: 'relative', minHeight: '100vh', padding: '24px', background: '#f9fafb', }} > <DraggableBox id="box-1">拖拽盒子 1</DraggableBox> <DraggableBox id="box-2">拖拽盒子 2</DraggableBox> <DraggableBox id="box-3">拖拽盒子 3</DraggableBox> </div> </DndContext> ); }
关键细节说明
- 父容器必须设置
position: relative,确保组件的绝对定位参考系正确 - 初始渲染时组件用
static定位(正常流),计算完位置后切换为absolute,保证初始布局符合预期 - 计算相对位置时加入父容器的
scrollLeft和scrollTop,解决窗口滚动导致的位置偏差问题 - 拖拽时通过Dnd Kit的
transform属性处理实时偏移,与初始位置结合实现平滑拖拽
内容的提问来源于stack exchange,提问作者akaparadox
相关产品推荐
相关产品推荐

