본문 바로가기
  • ANALOG CODE
  • AnalogCode
개발

Javascript 터치 이벤트 Offset 구하기

by 아날로그코더 2023. 4. 23.
반응형

Javascript로 모바일 브라우저에서 터치 이벤트를 처리하기 위해 좌표를 구하려고 하면 다양한 기준의 좌표값이 있다. 정해진 영역내에서의 터치이벤트의 상대좌표를 알기 위해서는 별도로 Offset을 계산하는 처리가 필요하다. 터치이벤트에 대해 먼저 간략히 알아보자.

 

 

Javascript Touch Event

 

일반적인 웹사이트를 개발하다보면 터치이벤트를 처리해야될 상황이 사실 거의 없다. 하지만 HTML Canvas를 이용하여 무언가를 그리는 사이트를 만들게 되면 모바일 디바이스에서의 원활한 동작을 위해 터치이벤트를 처리해줘야 한다. PC에서라면 마우스 스크롤 이벤트를 이용하여 확대/축소 기능을 구현할 수 있지만, 모바일 디바이스에서는 두손가락 터치를 이용하여 확대/축소를 해야한다.

 

두손가락 터치로 확대/축소 기능을 개발하기 위해서는 터치 좌표를 가져와야 한다. 그런데 터치 오브젝트에서 타겟이 되는 DOM Element  기준의 좌표값이 존재하지가 않는다. 마우스 이벤트의 경우에는 타겟 노드를 기준으로한 offsetX/Y 값을 제공하는데 터치 이벤트에는 이 값이 없다. 터치이벤트에 대해 먼저 알아보고 offset 좌표값을 어떻게 계산해야되는지를 알아보겠다.

 

 

터치 이벤트

터치 이벤트란 터치로 인한 움직임을 감지할 능력을 제공한다. 이벤트는 손가락이 터치 표면에 처음 닫는 순간부터 시작하며 터치한 상태로 이동할 수 있고 손가락을 때는 순간 이벤트가 종료한다. 동시에 여러 손가락으로 멀티 터치가 가능하며, 각각의 터치 포인트에 대한 정보를 모두 포함한다.

 

아래는 터치이벤트의 종류이다. 멀티 터치가 가능하다는 것만 빼면 마우스 이벤트와 비슷하다.

touchstart 터치가 시작하는 순간 발생하는 이벤트
touchmove 터치를 시작한 상태에서 터치 지점을 움직이면 발생하는 이벤트
touchend 터치를 때면 발생하는 이벤트
touchcancel 터치 포인트가 방해를 받아 취소될 경우 발생하는 이벤트
 - modal alert창이 뜨는 경우
 - 터치한 상태에서 외부로 포커스가 옮겨갈 경우
 - 브라우저에서 지원하는 터치 포인트의 갯수를 초과한 경우

touchcancel 이벤트가 발생하는 상황에서 touchend 이벤트를 주지는 않는다. 모바일 디바이스에서 touchcancel 이벤트가 발생할 만한 상황이 많지는 않지만, 그래도 만약에 드래그 중이거나 확대/축소 상황에서 touchcancel 이벤트가 발생해서 touchend와 같이 터치 핸들러를  중지해주는 처리를 하고 싶다면 touchcancel에 대한 핸들러도 구현해주어야 한다.

 

다음은 터치 이벤트 발생시의 이벤트 객체를 살펴보자.

 

 

TouchEvent Object

터치 이벤트가 발생하였을때 주는 이벤트 객체로써, 터치 이벤트 발생시의 상태값을 포함하고 있다.

 

elem.addEventListener('touchstart', (e) => {
  // e: TouchEvent
})

 

TouchEvent 객체는 아래와 같은 속성들을 가진다. 

altKey alt 키가 눌렸는지를 나타내는 Boolean 값
ctrlKey ctrl 키가 눌렸는지를 나타내는 Boolean 값
metaKey Command키(⌘ MacOS), Window키(⊞ 윈도우) 가 눌렸는지를 나타내는 Boolean 값
shiftKey shift 키가 눌렸는지를 나타내는 Boolean 값
changedTouches Touch Object 리스트 (상태 변화가 있는)
targetTouches Touch Object 리스트 (target element가 같은)
touches Touch Object 리스트 (상태 변화나 target에 상관없이 현재 터치되는 모든 지점)

대부분의 상황이라면 touches 만 가지고 처리를 할 수 있다. 특히나 모바일 디스바이스라면 터치를 하면서 다른 키를 누를일이 거의 없을거라 생각된다.

 

■ 터치 이벤트 샘플

아래영역을 터치하면  이벤트객체의 타입과  changedTouches, targetTouches, touches 리스트의 터치 개수를 표시하여 준다.

 

영역을 터치하세요

 

 

 

이제는 터치 포인트 하나를 나타내는 Touch Object를 자세히 살펴보자. 

 

 

Touch Object

하나의 터치 포인트에 대한 정보를 담고 있는 객체이다. 동시에 두손가락으로 터치를 한다면 Touch Object가 2개가 생성되는 것이다. 즉 Touch Object 하나가 내 손가락 하나이다. 우리는 여기에 있는 좌표 정보를 주로 사용하게 된다.

identifier 각각의 터치를 구분하고 추적할 수 있도록 만드는 고유 식별자
clientX / clientY 브라우저 viewport 기준의 좌표
pageX / pageY document 기준의 좌표 (scroll 영역이 포함된다)
screenX / screenY 모니터 스크린 기준의 좌표
target 터치가 처음 발생한 Element
radiusX / radiusY 터치 영역의 X / Y 반경
rotationAngle 터치 영역의 회전각
force 터치 영역의 압력 (0~1 사이의 실수)

 

- identifier

 각 터치 포인트마다 고유한 ID 값을 부여한다. 만약에 터치로 선을 그리는 동작을 구현한다고 할 경우 동시에 두손가락으로 선을 그리게 하려면 각 터치의 고유ID를 추적해서 각각의 ID에 따라 선을 이어주어야 할 것이다.

- 좌표계 

좌표계 때문에 혼돈이 왔었다. MouseEvent에 있는 offsetX / offsetY 속성이 없기 때문이다.

clientX, clientY를 사용하였더니 터치 타겟 영역 기준의 좌표가 아닌 브라우저의 뷰포트 기준으로 좌표값을 가져와서 타겟 영역 기준의  offset 좌표값을 계산해주어야 한다.

 

 

아래 3개의 속성은 터치 영역에 대한 값이다. 일반적으로는 사용할 일이 많지는 않을 것 같다.

- radiusX, radiusY

  터치한 표면의 면적을 근접한 타원으로 나타내서 타원의 X, Y 반경을 반환한다.

 

- rotationAngle

  터치 표면 타원의 회전각이라고 한다. 테블릿을 전용펜으로 터치하면 값이 바뀐다.

 

- force

터치를 누를때의 압력을 나타낸다. 테블릿을 전용펜으로 터치하면 값이 바뀐다. 손가락으로 할때는 항상 1이 나오는데, 아마도 디바이스마다 다르지 않을까 생각된다.

 

■ radius / rotationAngle / force 테스트

아래 영역을 각종 디바이스에서 터치해보면 값이 바뀌는걸 알 수 있다.

 

영역을 터치하세요
이벤트:
radiusX:
radiusY:
rotationAngle:
force:

 

 

offset 좌표 계산하기

자 그럼 이제 타겟 영역 기준의 터치 좌표를 계산하려면 어떻게 해야할지를 알아보자. 

Element.getBoundingClientRect() 를 사용하면 된다.

 

Element.getBoundingClientRect()

 

이 함수는 뷰포트 기준의 상대적인 위치 정보를 준다. 그러면 Touch Object에서 뷰포트 기준으로 좌표를 주는 clientX, clientY 값을 사용하여 차이를 계산하면 된다.

 

아래 코드처럼 Touch Object 의 target 엘리먼트의 위치를 가져와서 clientX/Y 값의 터치좌표를 빼주면 해당 영역내의 좌표가 계산된다.

// e: Touch Object
const offset = e.target.getBoundingClientRect()

const offsetX = e.touches[0].clientX - offset.x
const offsetY = e.touches[0].clientY - offset.y

 

■ 좌표 계산 샘플

아래 영역을 터치하면 4가지 좌표값 기준의 터치 좌표를 보여준다.

  • screenXY: 모니트 스크린 기준 좌표
  • pageXY: document 기준의 좌표
  • clientXY: viewport 기준의 좌표
  • offsetXY: element 기준의 좌표

 

영역을 터치하세요
이벤트:
screenXY:
pageXY:
clientXY:
offsetXY:

 

offsetXY 값이 target 영역을 기준으로 나오는 걸 볼 수 있다.

 

 

 

 

반응형

댓글