주메뉴 바로가기 견적문의 바로가기 주내용 바로가기
Blue Red Orange Green Grey
Mobile/HTML5
터치 이벤트를 이용한 사용자 제스처 분석
입력 2013.03.26 오후 02:18 | 수정 2013.03.26 오후 02:35 | 조회 2,884
x-mind
NHN 모바일Ajax팀 오혜진 |   http://helloworld.naver.com/helloworld/80243
모바일 웹 페이지에서는 더블탭, 롱탭에 대한 이벤트가 없기 때문에 터치 이벤트로 사용자의 이벤트를 분석해야 합니다. 또한 플리킹이나 드래그 앤드 드롭 같은 기능을 구현할 때에도 마우스 이벤트가 아닌 터치 이벤트를 사용합니다. 이 글은 터치 이벤트에 대해 알아보고, 터치 이벤트로 사용자 제스처를 분석하는 방법을 설명합니다.

터치 이벤트

모바일 브라우저에서는 마우스 이벤트와 별도로 터치 이벤트를 지원한다. 터치 이벤트는 화면에 손가락을 터치했을 때 발생하는 이벤트로 다음 표와 같은 종류가 있다.
표 1. 터치 이벤트의 종류
이벤트 이름
설명
touchstart
스크린에 손가락이 닿을 때 발생한다
touchmove
스크린에 손가락이 닿은 채로 움직일 때 발생한다
touchend
스크린에서 손가락을 뗄 때 발생한다
touchcancel
시스템에서 이벤트를 취소시킬 때 발생한다. 정확한 발생 조건은 브라우저마다 다르다.
터치를 취소한다는 것에 대한 표준이 정의되지 않아 각 브라우저마다 다르게 발생하여 touchend 이벤트로 간주해도 무방하다.
터치 이벤트와 마우스 이벤트는 몇 가지 차이가 있다.
  • 터치 이벤트는 사용자가 2, 3개의 손가락으로 화면을 조작할 때처럼 2개 이상의 개별 터치로 구성되는 경우가 있어 각 터치에 대한 정보를 모두 포함한다(안드로이드 3.0 버전 미만 브라우저 제외).
  • 정확한 좌표 값을 얻을 수 있는 마우스 이벤트와 달리 터치 이벤트는 손가락 접촉면이 크기 때문에 접촉 표면의 평균 좌표 값을 얻는다.
  • Mouseover 이벤트에 해당하는 터치 이벤트가 없다.
각 터치와 터치에 대한 정보는 이벤트 객체의 touches 속성과 targetTouches 속성, changedTouches 속성에 배열 형태로 저장되며, 터치한 손가락에 개수에 따라 배열의 크기가 결정된다. 멀티터치를 지원하지 않는 안드로이드 3.0 미만의 브라우저에서는 배열의 크기는 항상 1이다.
배열에 저장된 객체는 Touch 타입의 객체이며 마우스 이벤트와 거의 차이가 없다. 아래 표는 Touch 객체의 속성을 정리한 표이다.
표 2. Touch 객체 속성
속성
설명
identifier
인식 점을 구분하기 위한 인식 점 번호
screenX
디바이스 화면을 기준으로 한 X 좌표
screenY
디바이스 화면을 기준으로 한 Y 좌표
clientX
브라우저 화면을 기준으로 한 X 좌표 (스크롤 미포함)
clientY
브라우저 화면을 기준으로 한 Y 좌표 (스크롤 미포함)
pageX
가로 스크롤을 포함한 브라우저 화면을 기준으로 한 X 좌표
pageY
세로 스크롤을 포함한 브라우저 화면을 기준으로 한 Y 좌표
target
터치된 DOM 객체
참고
현재 터치 이벤트는 WebKit 계열의 브라우저(사파리 모바일, 안드로이드 브라우저, 돌핀)에서만 지원하고 그 외 브라우저(오페라미니, 파이어폭스, 인터넷 익스플로러 모바일 등)에서는 지원하지 않는다.
멀티터치는 touchstart 이벤트와 touchend 이벤트를 활용해서는 구별하기 힘들다. 손가락으로 터치하는 특성상 한 번에 두 개를 터치해도 시스템적으로 터치 시점에 차이가 있기 때문에 touchstart 이벤트와 touchend 이벤트 시점을 구별하기 힘들다. 멀티터치를 구현할 때에는 touchmove 이벤트를 활용하기를 권장한다.

사용자 제스처 분석

이번 장에서는 아래와 같은 기능을 터치 이벤트를 이용해 구현하는 방법을 알아본다.
  • 더블탭
  • 롱탭
  • 사용자 터치 방향 분석
각 기능들을 구현하기 위해서 아래 코드처럼 터치 이벤트에 각각의 핸들러를 등록한다. 이 글의 코드 예제는 Jindo 프레임워크를 사용해서 작성했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function onStart(e) {
}
function onMove(e) {
}
function onEnd(e) {
}
function onCancle(e) {
}
jindo.$Fn(onStart, this).attach(element, "touchstart");
jindo.$Fn(onMove, this).attach(element, "touchmove");
jindo.$Fn(onEnd, this).attach(element, "touchend");
jindo.$Fn(onCancle, this).attach(element, "touchcancle");

touchmove 이벤트가 발생하지 않고 touchstart 이벤트, touchend 이벤트의 순서로 이벤트가 발생할 때 탭이라고 판단한다.
아래 그림은 탭 이벤트를 분석하는 플로차트이다.
8369f6aed8df4917c43731466f301950.png
그림 1 터치 이벤트를 이용한 클릭 이벤트 분석
다음은 탭 이벤트를 분석하는 예제이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var bStartEvent = false;
//touchstart 이벤트 발생 여부 플래그
var bMoveEvent = false;
//touchmove 이벤트 발생 여부 플래그
function onStart(e) {
bStartEvent = true;
}
function onMove(e) {
if(!bStartEvent) {
return;
//touchstart 이벤트가 발생하지 않으면 처리하지 않는다.
}
bMoveEvent = true;
//touchMove 이벤트 발생 여부를 설정한다.
}
function onEnd(e) {
if(bStartEvent && !bMoveEvent) {
//클릭 이벤트로 판단한다.
alert('클릭 이벤트');
}
//각 플래그 값을 초기값으로 설정한다.
bStartEvent = false;
bMoveEvent = false;
}

더블탭

탭 이벤트가 두 번 발생하면 더블탭으로 판단한다. 두 개의 탭 이벤트 간의 기준 시간은 개발자가 설정할 수 있으며, 이 예제에서는 1000ms로 설정했다. 또한 손가락 면적이 넓어 정확히 같은 좌표를 누르기는 힘들기 때문에 같은 탭으로 인지하는 임계 값을 설정해 비교한다.
아래 그림은 더블탭 이벤트를 분석하는 플로차트이다.
e10ed3e65a82c554e523d7bf34811ace.png
그림 2 터치 이벤트를 이용한 더블탭 이벤트 분석
다음은 더블탭 이벤트를 분석하는 예제이다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
var bStartEvent = false; //touchstart 이벤트 발생 여부 플래그
var bMoveEvent = false; //touchmove 이벤트 발생 여부 플래그
var htClickInfo = { //더블탭을 판단하기 위한 마지막 탭 이벤트의 정보 해시 테이블
sType : null,
nX : -1,
nY : -1,
nTime : 0
}
var nDoubleTapDuration = 1000; //더블탭을 판단하는 기준 시간(ms)
var nTapThreshold = 5; //탭을 판단하는 거리
function initClearInfo() {
htClickInfo.sType = null;
}
function onStart(e) {
bStartEvent = true;
}
function onMove(e) {
if(!bStartEvent) {
return; //touchstart 이벤트가 발생하지 않으면 처리하지 않는다.
}
bMoveEvent = true; //touchmove 이벤트 발생 여부를 설정한다.
}
function onEnd(e) {
var nX = e.$value().changedTouches[0].pageX;
var nY = e.$value().changedTouches[0].pageY;
var nTime = e.$value().timeStamp;
if(bStartEvent && !bMoveEvent) {
//이전 탭 이벤트와 시간 차이가 1000ms 이하일 경우
if(htClickInfo.sType == 'click' && (nTime – htClickInfo.timeStamp) <= nDoubleTapDuration){
if( (Math.abs(htClickInfo.nX-nX) <= nTapThreshold) && (Math.abs(htClickInfo.nY-nY) <= nTapThreshold) ){
//더블탭으로 판단한다.
}
} else {
//탭 이벤트로 판단한다.
//현재 탭 이벤트들에 대한 정보를 업데이트한다.
htClickInfo.sType = 'click';
htClickInfo.nX = nX;
htClickInfo.nY =nY;
htClickInfo.nTime = nTime;
}
} else {
//탭 이벤트가 아니므로 탭 이벤트 정보를 초기화한다.
initClearInfo();
}
//각 플래그 값을 초기값으로 세팅한다.
bStartEvent = false;
bMoveEvent = false;
}

롱탭

touchstart 이벤트 발생 후, 기준 시간 내에 touchmove 이벤트와 touchend 이벤트가 발생하지 않으면 롱탭으로 판단한다. 기준 시간은 개발자가 수정할 수 있으며, 이 예제에서는 1000ms로 설정했다.
아래 그림은 롱탭을 분석하는 플로차트이다.
cf4baae11415f48168c45408c87efba4.png
그림 3 터치 이벤트를 이용한 롱탭 이벤트 분석
다음은 롱탭 이벤트를 분석하는 예제이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function startLongTapTimer() { //롱탭을 판단하는 타이머를 실행하는 코드
var self = this;
this.nLongTabTimer = setTimeout(function() {
//롱탭 이벤트로 판단한다.
alert('롱탭');
delete self.nLongTabTimer;
}, 1000)
}
function deleteLongTabTimer() {
//활성화된 롱탭 타이머를 삭제하는 코드
if( typeof this.nLongTabTimer !== 'undefined') {
clearTimeout(this.nLongTabTimer);
delete this.nLongTabTimer;
}
}
function onStart(e) {
startLongTapTimer(); //롱탭을 판단하기 위한 타이머를 활성화
bStartEvent = true;
}
function onMove(e) {
if(!bStartEvent) {
return
}
deleteLongTabTimer(); //touchmove 이벤트가 발생했기 때문에 롱탭 타이머 삭제
}
function onEnd(e) {
if(!bStartEvent) {
return
}
deleteLongTabTimer(); //touchend 이벤트가 발생했기 때문에 롱탭 타이머 삭제
bStartEvent = false;
}

사용자 터치 방향 분석

플리킹이나 스크롤 기능을 구현하려면 사용자가 터치해서 움직이는 방향이 수직 방향인지 수평 방향인지 판단해야 한다. 사용자 움직임의 방향을 판단하는 기준은 다음과 같이 여러 가지가 있다.
  • 사용자가 기준거리(픽셀)를 움직였을 때 수직, 수평 방향의 움직인 거리를 비교하여 판단
  • 사용자가 기준거리(픽셀)를 움직였을 때 수직, 수평 방향의 움직인 거리를 기울기로 비교하여 판단.
이 예제에서는 두 번째를 판단 기준으로 선택했다. 첫 번째 기준을 적용할 경우 수직과 수평 방향의 움직임 거리의 차이가 1픽셀과 같이 작을 경우에도 적용되기 때문에 기울기를 이용하여 판단하는 방법을 적용했다. 기준 기울기는 모바일 단말기의 화면 사이즈를 이용하여 설정한다.
아래 그림은 단말기화면에서 사용자 움직임을 판단하는 기준기울기이다.
66c92a1f1265941e5f5ae457298a122a.jpg
그림 4 단말기 화면에 따른 기준 기울기
  • 수평 방향 기준 기울기 = (모바일 단말기 세로 너비/2) / 모바일 단말기 가로 너비
  • 수직 방향 기준 기울기 = 모바일 단말기 세로 너비 / (모바일 단말기 가로 너비/2)
사용자가 기준 거리 이상 움직였을 때, 움직인 수직, 수평 방향의 거리를 이용해 현재 사용자 움직임 기울기를 구하고, 그 기울기가 수평 방향 기준 기울기보다 크면 수직 방향으로 판단하고, 작으면 수평 방향으로 판단한다. 기울기를 판단하는 기준 거리는 개발자가 설정할 수 있으며, 이 예제에서는 25픽셀로 설정했다.
참고
가로 플리킹이나 스크롤에 적용할 때는 기울기를 판단하는 기준 거리를 4픽셀로 잡는 것이 좋다. 그 이상으로 값을 설정하면 기본 수직 스크롤이 발생하여 touchmove 이벤트나 touchend 이벤트가 발생하지 않을 수 있기 때문이다.
아래 그림은 사용자 터치 움직임의 방향성을 판단하는 플로차트이다.
8b788da73cab8cbf45bd43133c8138f8.png
그림 5 터치 이벤트를 이용한 사용자 드래그 방향성 분석
다음은 사용자의 움직임 방향을 판단하는 예제이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
var bStartEvent = false; //touchstart 이벤트 발생 여부 플래그
var nMoveType = -1; //현재 판단된 사용자 움직임의 방향
var htTouchInfo = { //touchstart 시점의 좌표와 시간을 저장하기
nStartX : -1,
nStartY : -1,
nStartTime : 0
};
//수평 방향을 판단하는 기준 기울기
var nHSlope = ((window.innerHeight / 2) / window.innerWidth).toFixed(2) * 1;
function initTouchInfo() { //터치 정보들의 값을 초기화하는 함수
htTouchInfo.nStartX = -1;
htTouchInfo.nStartY = -1;
htTouchInfo.nStartTime = 0;
}
//touchstart 좌표값과 비교하여 현재 사용자의 움직임을 판단하는 함수
function getMoveType(x, y) {
//0은 수평방향, 1은 수직방향
var nMoveType = -1;
var nX = Math.abs(htTouchInfo.nStartX - x);
var nY = Math.abs(htTouchInfo.nStartY - y);
var nDis = nX + nY;
//현재 움직인 거리가 기준 거리보다 작을 땐 방향을 판단하지 않는다.
if(nDis < 25) { return nMoveType }
var nSlope = parseFloat((nY / nX).toFixed(2), 10);
if(nSlope > nHSlope) {
nMoveType = 1;
} else {
nMoveType = 0;
}
return nMoveType;
}
function onStart(e) {
initTouchInfo(); //터치 정보를 초기화한다.
nMoveType = -1; //이전 터치에 대해 분석한 움직임의 방향도 초기화한다.
//touchstart 이벤트 시점에 정보를 갱신한다.
htTouchInfo.nStartX = e.$value().changedTouches[0].pageX;
htTouchInfo.nStartY = e.$value().changedTouches[0].pageY;
htTouchInfo.nStartTime = e.$value().timeStamp;
bStartEvent = true;
}
function onMove(e) {
if(!bStartEvent) {
return
}
var nX = e.$value().changedTouches[0].pageX;
var nY = e.$value().changedTouches[0].pageY;
//현재 touchmMove에서 사용자 터치에 대한 움직임을 판단한다.
nMoveType = getMoveType(nX, nY);
//현재 사용자 움직임을 수직으로 판단해 기본 브라우저의 스크롤 기능을 막고 싶으면 아래 코드를 사용한다.
if(nMoveType === 1) {
e.stop(jindo.$Event.CANCLE_DEFAULT);
}
}
function onEnd(e) {
if(!bStartEvent) {
return
}
//touchmove에서 움직임을 판단하지 못했다면 touchend 이벤트에서 다시 판단한다.
if(nMoveType < 0) {
var nX = e.$value().changedTouches[0].pageX;
var nY = e.$value().changedTouches[0].pageY;
nMoveType = getMoveType(nX, nY);
}
bStartEvent = false;
nMoveType = -1; //분석한 움직임의 방향도 초기화한다.
initTouchInfo(); //터치 정보를 초기화한다.
}

마치며

모바일 웹에서도 모바일 애플리케이션에서와 같은 사용자 경험을 요구하는 사례가 많아지면서, 더블탭이나 롱탭으로 동작하는 UI를 구현해야 하는 일이 있다. 그러나 모바일 웹 브라우저는 아직 더블탭 이벤트나 롱탭 이벤트를 지원하지 않는다. 이 글에서는 터치 이벤트로 사용자의 이벤트를 분석해 더블탭이나 롱탭을 구현하는 방법을 설명했다. 마찬가지로 터치 이벤트를 분석해 사용자 제스처를 분석하는 방법을 알아봤다.

h_ohj.jpeg
NHN 모바일Ajax팀 오혜진
2011년부터 모바일 웹개발을 시작하였고 수많은 삽질과 시행착오를 겪으면서 JMC를 개발해왔습니다. 앞으로JMC 유저분들이 쉽고 빠르게 모바일 웹개발을 하실 수 있도록 노력을 기울이겠습니다.
Security number

click to select icon

  • Very Happy
  • Smile
  • Sad
  • Surprised
  • Shocked
  • Confused
  • Cool
  • Laughing
  • Mad
  • Razz
  • Embarassed
  • Crying or Very sad
  • Evil or Very Mad
  • Twisted Evil
  • Rolling Eyes
  • Wink
  • Exclamation
  • Question
  • Idea
  • Arrow
  • Sux
  • Good
  • Very Angry
  • Attention


목록수정삭제인쇄0


Board List
No.제목글쓴이조회등록일
실무에서 많이 쓰는 위주로 순서없이 그때 그때 포스트합니다.x-mind6372015.04.02
HTML5강좌 링크x-mind1,7802012.02.28
23네이버 웹마스터 도구로 사이트 검색엔진에 추가x-mind2642016.04.26
22[HTML5] 안드로이드 및 아이폰에서 input file 사용시 카메라 호...x-mind8522016.04.22
21유투브 동영상을 배경으로x-mind3732016.03.30
20Canvas - 파일로 저장x-mind4992015.09.08
19모바일웹 카카오링크 연동(단순텍스트 링크 보내기)x-mind1,0462015.06.11
18[링크]나의 웹사이트에 Facebook 계정으로 로그인 하기x-mind9152015.01.22
17[HTML5] 위치정보 사용하기x-mind1,0472014.10.27
16모바일 브라우저 테스트 도구x-mind1,3912014.03.20
15Safari 브라우저 모바일 모드x-mind1,5182014.03.20
14iScroll을 사용해서 모바일웹에서 내부 스크롤 영역 만들기x-mind1,8442013.03.27
13터치 이벤트를 이용한 사용자 제스처 분석x-mind2,8842013.03.26
12모바일 웹 스크롤 페이징x-mind7,8282013.03.26
11웹표준, 웹접근성을 고려한 HTML 코딩 작업시 규칙.x-mind3,5422013.01.24
10Google Chart Api 이용 QR 코드 생성x-mind1,3572013.01.24
9웹표준 코딩시 주의할점x-mind2,7392012.07.26
8tabindex 속성이 오히려 접근성을 해친다.x-mind2,1672012.04.11
7아이폰, 안드로이드 바탕화면에 바로가기 만들기 4x-mind9,6282012.03.27
6이전버전에서의 HTML5요소 사용x-mind1,0212012.03.02
5HTML5 API 강좌 #1 – Web Storage 와 Application Cachex-mind1,2492012.02.28
4화면 사이즈에 맞게 이미지 확대x-mind4,1942012.01.27
Copyrightⓒ x-mind.co.kr All rights reserved. 1