未手动定位时,如何获取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
相关产品推荐
相关产品推荐

