목표
드래그로 엘리먼트 이동시키기
결과물
https://codesandbox.io/s/drag-and-drop-ykyxgd?file=/src/Moving.tsx
조건
1. pointer event 사용
- mouse event 와 기능은 거의 동일하나, 모바일 디바이스에서 touch를 지원
- 따라서 mouse event의 상위 호환
2. 마우스 커서의 속도에 맞춰서 따라와야 함
- PointerEvent의 movementX, movementY 프로퍼티로 타겟 엘리먼트를 translate시키는 방식은 속도가 느려서 마우스 커서가 쉽게 엘리먼트 바깥으로 나가버리고, 드래그가 끊겨버림..
그런데
onMouseMove, onPoinerMove 는 onDragStart에 비해 속도가 느리다...
참고)
draggable로 인한 반투명 드래깅 UI를 주기 싫고, 터치 드래그 효과를 주고 싶었지만, 속도 이슈가 있어서 어쩔 수 없이 drag event 사용...
그런데! 전역에 이벤트 핸들러를 등록하면 된다?!
https://velog.io/@bepyan/Drag-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%BD%80%EA%B0%9C%EA%B8%B0
1. 클릭 시 전역에 pointermove 이벤트 핸들러(커서 움직임 추적하여 엘리먼트 좌표 업데이트) 등록, pointerup 이벤트 핸들러(등록된 이벤트 핸들러 제거) 등록
2. 클릭된 상태에서는 이벤트 핸들러가 등록되어 있으므로 마우스 움직이면 계속 엘리먼트 새 좌표 업데이트됨
3. pointerup 이벤트가 실행되면 pointermove 이벤트 핸들러 제거 (1번만 발동되면 됨)
코드
import { useState } from "react";
export default function Moving() {
const [diffPos, setDiffPos] = useState({
x: 0,
y: 0
});
return (
<div
className="window"
onPointerDown={(e) => {
const initX = e.clientX;
const initY = e.clientY;
const dragMove = (e: PointerEvent) => {
const newDiffPos = {
x: diffPos.x + e.clientX - initX,
y: diffPos.y + e.clientY - initY
};
setDiffPos(newDiffPos);
};
const dragEnd = (e: PointerEvent) => {
document.removeEventListener("pointermove", dragMove);
};
document.addEventListener("pointermove", dragMove);
document.addEventListener("pointerup", dragEnd, { once: true });
}}
style={{
transform: `translateX(${diffPos.x}px) translateY(${diffPos.y}px)`
}}
>
<p>hahahahahahahahaha</p>
<p>hahahahahahahahaha</p>
<p>hahahahahahahahaha</p>
<p>hahahahahahahahaha</p>
</div>
);
}