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

如何在未知初始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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 03:57:08