Utility: drag events

올리·2023년 12월 10일
0

소개

이번에는 다른 분의 코드를 거의 그대로 가져왔다. 비록 플젝에는 복붙해 넣었지만 블로그에도 따로 코드를 올려도 되나? 싶었는데 내 프로젝트에 맞게 아주 살짝 변형한 부분이 있어 일단 원본 링크를 걸고 올려보기로 했다. 링크에서는 다양한 드래그들도 많고, codepen을 통해 예제 코드를 바로 실행해볼 수 있다!

https://inpa.tistory.com/entry/드래그-앤-드롭-Drag-Drop-기능#드래그_객체_이동 [Inpa Dev 👨‍💻:티스토리]

나름 라이브러리 덜 쓰고 만들어보자 했는데(즉 혼자서 다 해보기).. 점점 처음의 포부와는 좀 멀어지는 느낌...
그래도 코드를 살펴본 데 의의를 둬 본다...

내 프로젝트에 적용

touchmove처럼 모바일 환경을 고려한 부분을 빼고, 내 프로젝트 요소에 적용할 수 있게끔 구조를 조금 수정했다.

// 전체 배경화면
const desktop = document.querySelector('#desktop'); 

const createDraggable = (dragItem) => {
    let active = false;
    let currentX;
    let currentY;
    let initialX;
    let initialY;
    let xOffset = 0;
    let yOffset = 0;

    const dragStart = (e) => {
        initialX = e.clientX - xOffset;
        initialY = e.clientY - yOffset;

        if (e.target === dragItem) active = true;
    };

    const dragEnd = () => {
        initialX = currentX;
        initialY = currentY;
        active = false;
    };

    const drag = (e) => {
        if (active) {
            e.preventDefault();

            currentX = e.clientX - initialX;
            currentY = e.clientY - initialY;

            xOffset = currentX;
            yOffset = currentY;

            setTranslate(currentX, currentY, dragItem);
        }
    };

    const setTranslate = (xPos, yPos, el) => {
        el.style.transform = 'translate3d(' + xPos + 'px, ' + yPos + 'px, 0)';
    };

    dragItem.addEventListener('dragstart', dragStart);
    desktop.addEventListener('drop', dragEnd);
    desktop.addEventListener('dragover', drag);
    return dragItem;
};

export { createDraggable };
  • 드래그 대상인 dragItem에는 dragStart 이벤트에 대한 리스너를 붙여준다. 드래그 대상 요소의 위치를 기준으로 시작 위치를 초기화해야하기 때문이다.

  • 나머지 이벤트들은 desktop, 즉 화면 요소에 달아준다.

  • 원래 내 프로젝트에서는 별도의 객체나 클래스를 사용하지 않고 있었는데, 드래그 이벤트는 요소별로 그 상태와 위치를 저장하고 있어야 했다. 모든 요소 코드에 중복된 코드를 넣을 수는 없으니 유틸리티로 따로 관리해주면서, 요소별 상태 저장을 해 주기 위해서는 createDraggable 안에 변수와 그를 이용하는 함수들을 하나의 객체 안에 묶어 줘야 했다.

// 명언 요소를 바탕화면에서 클릭했을 때, 
const clickQuoteLabel = () => {
  ... 
  quoteEl = createlifeQuoteEl(); // 요소 생성
  ...
  addWindowElToDesktop(quoteEl); // 드래그 이벤트 붙여주기
};

호출은 역시 각 요소를 만들 때 이루어진다.

lifeQuoteEl.draggable = true;

참고로 드래그 대상 요소의 draggable 속성을 true로 해줘야 드래그가 제대로 동작한다!

코드 분석

링크된 포스팅에서 앞서 개념들을 소개하고 있지만 개별 응용 코드에 대해서는 자세한 설명이 없어서 공부할 겸 정리했다.

변수

  • active
    드래그가 활성화됐는지를 표시. 마우스로 요소를 클릭하고 드래그하면 true, 놓으면 false가 된다.
  • currentX, currentY
    드래그중인 마우스 포인터의 x,y 위치로, px 단위이다. drag 함수가 호출될때마다 갱신된다.
  • initialX, initialY
    드래그를 시작했을 때, 즉 초기 x,y 위치값이다. 드래그가 끝날 때까지만 유지된다.
  • xOffset, yOffset
    드래그 요소의 x축, y축 기준 이동 거리를 의미한다. 역시 단위는 px이다.

함수 동작 매커니즘

  • dragItem을 클릭하면 dragStart 함수가 실행된다.
    이 함수는 클릭 지점의 위치를 initialXinitialY에 저장하고, xOffsetyOffset을 사용해 이미 이동한 거리를 계산한 뒤 초기 위치에서 뺀다. activetrue로 설정되어 있으면 드래그 동작이 활성화된다.

  • 마우스를 움직이면 drag 함수가 실행된다. 이 함수는 currentXcurrentY 변수를 사용해 마우스의 새 위치를 계산하고, xOffsetyOffset에 새 위치를 저장한 뒤setTranslate 함수를 호출해서 실제 HTML 요소의 위치를 화면에서 이동하게끔 한다.

  • 사용자가 마우스 버튼을 놓으면 dragEnd 함수가 실행된다. 이 함수는 activefalse로 설정하여 드래그 동작을 중지하고, '드롭'의 위치를 initialXinitialY에 저장해 다음 드래그 작업을 위한 initialX, initialY로 설정한다.

  • translate3d
    CSS의 transform 속성의 일부로, 3D 변환 함수 중 하나이며 주로 요소를 3차원 공간에서 이동시키기 위해 사용된다. 이 함수는 GPU 가속을 사용하여 렌더링되므로 요소의 이동이 더 부드럽게 보일 수 있다.

드래그 이벤트

  • dragStart
    드래그를 시작할 때 발생한다.

  • dragEnd
    드래그를 마치고 마우스 버튼을 놓는 순간 발생한다.

  • drag
    드래그 도중에 발생한다.

dragEnddrop이 헷갈렸는데 후자는 다른 요소와 상호작용(드래그된 요소를 다른 요소 위에 놓을 때)할 때 사용한다. 여기서는 단독 드래그 이동이어야 하므로 drop이나 dragover 이벤트는 사용하지 않는다.

0개의 댓글