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

未手动定位时,如何获取dndkit中可放置容器的坐标?

问题:dndkit中动态排列容器的坐标获取与拖拽放置适配

我正在使用dndkit创建多个可拖拽卡片与可放置容器,容器未通过CSS手动定位,而是随DOM自动排列(比如顶部添加组件时容器会自动下移)。拖拽卡片到容器时需要实现正确放置,以下是简化后的React代码:

return (
  <div className="">
    <DndContext onDragEnd={handleDragEnd} sensors={sensors}>
      <SomeComponent1/>
      <SomeComponent2/>
      <SomeComponent3/>
      {existingItems.map((item) => (
        <DraggableBox
          key={item.id}
          id={item.id}
          position={item.position}
          setPosition={(newPos) =>
            setExistingItems((prev) =>
              prev.map((i) =>
                i.id === item.id ? { ...i, position: newPos } : i
              )
            )
          }
        />
      ))}
      <ShelfPart items={itemsInShelf} />
    </DndContext>
  </div>
);

在ShelfPart组件内的ShelfPlaceComponent中创建了可放置容器(已使用useDroppable等API):

<ShelfPlaceComponent id="droppableShelf" itemsInShelf={itemsInShelf}/>

请问在未手动设置x、y坐标的情况下,如何获取ShelfPlaceComponent内div容器的x、y坐标?我曾尝试在容器内生成卡片实现相对定位,但卡片无法移出容器范围,不符合需求。


解决方案

方法1:React useRef结合getBoundingClientRect实时获取坐标

在ShelfPlaceComponent内部,给目标容器绑定ref,通过浏览器原生API获取实时坐标,同时兼容dndkit的可放置节点绑定:

import { useRef, useEffect, useState } from 'react';
import { useDroppable } from '@dnd-kit/core';

const ShelfPlaceComponent = ({ id, itemsInShelf }) => {
  const { setNodeRef } = useDroppable({ id });
  const containerRef = useRef(null);
  const [containerCoords, setContainerCoords] = useState({ x: 0, y: 0 });

  // 合并ref:同时满足dndkit的可放置节点绑定和自定义坐标获取
  const combinedRef = (node) => {
    setNodeRef(node);
    containerRef.current = node;
  };

  // 封装坐标获取逻辑
  const updateContainerCoords = () => {
    if (!containerRef.current) return;
    const rect = containerRef.current.getBoundingClientRect();
    // 转换为文档全局坐标(视口坐标 + 滚动偏移)
    setContainerCoords({
      x: rect.left + window.scrollX,
      y: rect.top + window.scrollY,
    });
  };

  // 监听容器位置变化的场景
  useEffect(() => {
    updateContainerCoords();
    // 监听窗口滚动、缩放,以及容器依赖的状态变化
    window.addEventListener('scroll', updateContainerCoords);
    window.addEventListener('resize', updateContainerCoords);
    
    return () => {
      window.removeEventListener('scroll', updateContainerCoords);
      window.removeEventListener('resize', updateContainerCoords);
    };
  }, [itemsInShelf]); // 可添加顶部组件的状态等依赖,确保位置变化时重新计算

  return (
    <div ref={combinedRef} className="shelf-container">
      {/* 容器内部内容 */}
    </div>
  );
};

方法2:在拖拽结束时通过dndkit事件获取放置区坐标

在根组件的handleDragEnd回调中,直接获取放置区的DOM节点并计算坐标:

const handleDragEnd = (event) => {
  const { active, over } = event;
  // 判断拖拽目标是否为目标放置区
  if (over?.id === 'droppableShelf') {
    // 通过id或ref获取放置区节点
    const droppableNode = document.getElementById('droppableShelf');
    if (!droppableNode) return;

    const rect = droppableNode.getBoundingClientRect();
    const shelfGlobalCoords = {
      x: rect.left + window.scrollX,
      y: rect.top + window.scrollY,
    };

    // 计算卡片需要放置的目标位置(示例:居中放置在容器内)
    const targetPosition = {
      x: shelfGlobalCoords.x + (droppableNode.offsetWidth / 2) - (active.rect.width / 2),
      y: shelfGlobalCoords.y + 20, // 自定义容器内边距偏移
    };

    // 更新卡片位置的逻辑
    setExistingItems(prev => prev.map(item => 
      item.id === active.id ? {...item, position: targetPosition} : item
    ));
  }
};

关键注意事项

  • 避免静态坐标:容器位置随DOM动态变化,必须实时获取坐标,不能使用初始加载的静态值。
  • 监听动态变化:除窗口滚动、缩放,还要监听父组件的状态变化(如顶部组件增减),确保坐标实时更新。
  • 坐标转换:getBoundingClientRect()返回视口相对坐标,需加上window.scrollX/Y转换为文档全局坐标。

内容的提问来源于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 04:12:38