我正在尝试创建一个拖放功能,它是模态中的子组件,其中当前拖动的项目跟随鼠标的位置,但是,鼠标的位置被模态中的其他内容所偏移,而不是模态的一部分拖放组件。 这里是显示问题的 repl 我希望元素完全跟随光标。
基本元素结构如下:
<Main>
<Modal>
<h2>Modal Header</h2>
<DndComponent>
<div>Drag Me</div>
<div>Element that follows cursor</div>
</DndComponent>
</Modal>
</Main>
我尝试了
e.clientY
、e.offsetY
和 e.pageY
与组件容器中的 getBoundingClientRect
的不同组合。我不想在模式上放置 ref
,因为拖放功能将在许多不同的地方使用。我也尝试过同时使用 position: absolute
和 position: fixed
考虑将
position: absolute
应用于 dragitem
。这确保了元素的“开始”位置是相对于 boundsRef
元素的,这是相对于 mousePos
计算的位置。
const { useState, useRef } = React;
function App() {
const [isDragging, setIsDragging] = useState(false);
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
const boundsRef = useRef(null);
const handleMouseDown = () => {
setIsDragging(true);
};
const handleMouseMove = (e) => {
const bounds = boundsRef.current.getBoundingClientRect();
if (isDragging) {
setMousePos({ x: e.clientX - bounds.x, y: e.clientY - bounds.y });
}
};
return (
<main>
<div className="container" onPointerMove={handleMouseMove}>
<div className="modal">
<h3>Modal Header</h3>
<h4 style={{ marginBottom: "20px" }}>Sub Header</h4>
{/* start of child component */}
<div ref={boundsRef} style={{ position: "relative" }}>
<ul>
<li className="modal-content" onPointerDown={handleMouseDown}>
<p>Drag Me</p>
</li>
</ul>
{isDragging && (
<div
className="dragItem"
style={{
transform: `translate(${mousePos.x}px, ${mousePos.y}px)`,
}}
>
<p>Item Being Dragged</p>
</div>
)}
</div>
{/* end of child component */}
</div>
</div>
</main>
);
}
ReactDOM.createRoot(document.getElementById("app")).render(<App />);
* {
margin: 0;
padding: 0;
}
.container {
position: relative;
height: 100vh;
width: 100vw;
background-color:#fff;
}
.modal {
height: 50vh;
width: 50vw;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #ededed;
}
.modal-content {
user-select: none;
}
.modal-content:hover {
cursor: grab;
}
.dragItem {
position: absolute;
z-index: 1000;
left: 0;
top: 0;
pointer-events: none;
background-color: #8593ff24;
padding: 0.5rem 1rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="app"></div>