[TypeScript] 제스처로 대화하기. #8 - 팬(with. 드래그) 편

고라니드로고라니드로
2 min read

Table of contents

이번 편은 이전 편으로부터 이어집니다.

pointermove를 드래그 대상에 설정하면 포인터를 빠르게 이동할 경우, 대상을 놓치는 것을 느끼셨을 겁니다. 왜 이런 일이 벌어질까요? 상상해 보세요. 포인터가 이동했습니다. 충분히 이동했다면 요소 바깥으로 넘어갔을 겁니다. 이후 핸들러가 호출되고, 대상을 이동하면 포인터를 따라잡을 겁니다. 하지만 핸들러가 호출되기 전에 포인터가 한 번 더 이동하면 어떻게 될까요? 이번에도 충분히 이동했다면 대상이 이동한 자리에는 이미 포인터가 없을 수도 있습니다.

어떻게 문제를 해결할 수 있을까요? 두 가지 방법을 생각할 수 있습니다. 포인터를 캡처하거나 상자보다 더 넓은 범위의 대상에서 팬을 감지하는 것입니다. 또한 두 방법은 반대가 아니며 함께 사용할 수 있습니다. 포인터 캡처는 pointerdown에서 setPointerCapture를 사용하여 간단하게 수행할 수 있습니다. 따라서 본 포스트에서는 후자에 대해서만 다루도록 하겠습니다.

interface PanEvent {
    type: "panstart" | "pan" | "panend";
    pointerId: number;
    x: number;
    y: number;
    // ...
}

parentElement.addEventListener("pointerdown", e => {
    const event: PanEvent = {
        type: "panstart",
        pointerId: e.pointerId,
        x: e.clientX,
        y: e.clientY
    };

    // ...
});

팬 이벤트의 유형을 panstart, pan, panend로 구분하여 각각 초기화, 드래그, 정리의 용도로 활용할 수 있습니다. 위 코드에서는 pointerdown에 대해서만 나타내지만, 그 외에도 pointermove, pointerup에서도 각각의 유형을 동일하게 적용할 수 있습니다.

const holdMap = new Map<number, Element>();

onPan(e => {
    switch(e.type) {
        case "panstart":
            holdMap.set(e.pointerId, document.elementFromPoint(e.x, e.y)!);
            break;
        case "pan":
            // ...
            break;
        case "panend":
            holdMap.delete(e.pointerId);
            break;
    }
});

holdMap은 포인터가 현재 잡고 있는 요소를 저장합니다. panstart시 저장하고, panend 시 제거합니다. pan에서 이를 활용하여 실제로 움직일 요소를 파악할 수 있습니다. 감지는 더 넓은 범위에서 이루어지므로 이 범위에서 벗어나지 않는 한 포인터는 대상을 놓치지 않습니다.

내용은 다음 편에서 이어집니다. 읽어주셔서 감사합니다!

묻고 답하기

개인적인 판단에 의해 적절하다고 여겨지는 경우, 모두가 볼 수 있도록 이곳에 문답이 추가됩니다. 그렇지 않더라도 질문에 대한 답변은 별도로 이루어집니다.

0
Subscribe to my newsletter

Read articles from 고라니드로 directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

고라니드로
고라니드로