光标后元素的位置由容器内容偏移

问题描述 投票:0回答:1

我正在尝试创建一个拖放功能,它是模态中的子组件,其中当前拖动的项目跟随鼠标的位置,但是,鼠标的位置被模态中的其他内容所偏移,而不是模态的一部分拖放组件。 这里是显示问题的 repl 我希望元素完全跟随光标。

Showing the relation between cursor position and modal content

基本元素结构如下:

<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

reactjs drag-and-drop css-position css-transforms pointer-events
1个回答
0
投票

考虑将

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>

© www.soinside.com 2019 - 2024. All rights reserved.