OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 'use strict'; | |
6 | |
7 /** | |
8 * A class that listens for touch events and produces events when these | |
9 * touches form gestures (e.g. pinching). | |
10 */ | |
11 class GestureDetector { | |
12 /** | |
13 * Constructs a GestureDetector. | |
14 * @param {!Element} element The element to monitor for touch gestures. | |
15 */ | |
16 constructor(element) { | |
17 this.element_ = element; | |
18 | |
19 this.element_.addEventListener('touchstart', this.onTouchStart_.bind(this)); | |
20 this.element_.addEventListener('touchmove', this.onTouch_.bind(this)); | |
21 this.element_.addEventListener('touchend', this.onTouch_.bind(this)); | |
22 this.element_.addEventListener('touchcancel', this.onTouch_.bind(this)); | |
23 | |
24 this.pinchStartEvent_ = null; | |
25 this.lastEvent_ = null; | |
26 | |
27 this.listeners_ = { | |
28 'pinchstart': [], | |
29 'pinchupdate': [], | |
30 'pinchend': [] | |
31 }; | |
32 } | |
33 | |
34 /** | |
35 * Add a |listener| to be notified of |type| events. | |
36 * @param {string} type The event type to be notified for. | |
37 * @param {Function} listener The callback. | |
38 */ | |
39 addEventListener(type, listener) { | |
40 if (this.listeners_[type]) { | |
dpapad
2016/10/31 23:02:23
Since you are already using ES6 in this file ("cla
Kevin McNee - google account
2016/11/07 23:08:27
Done.
| |
41 this.listeners_[type].push(listener); | |
42 } | |
43 } | |
44 | |
45 /** | |
46 * Call the relevant listeners with the given |pinchEvent|. | |
47 * @private | |
48 * @param {Object} pinchEvent The event to notify the listeners of. | |
49 */ | |
50 notify_(pinchEvent) { | |
51 var listeners = this.listeners_[pinchEvent.type]; | |
dpapad
2016/10/31 23:02:23
Nit(optional): s/var/let throughout this file?
Kevin McNee - google account
2016/11/07 23:08:27
Done.
| |
52 var numListeners = listeners.length; | |
53 for (var i = 0; i < numListeners; i++) { | |
dpapad
2016/10/31 23:02:23
Nit
for (let l of listeners)
l(pinchEvent);
Kevin McNee - google account
2016/11/07 23:08:27
Done.
| |
54 listeners[i](pinchEvent); | |
55 } | |
56 } | |
57 | |
58 /** | |
59 * The callback for touchstart events on the element. | |
60 * @private | |
61 * @param {!TouchEvent} ev Touch event on the element. | |
62 */ | |
63 onTouchStart_(ev) { | |
dpapad
2016/10/31 23:02:23
Let's be consistent within this file, s/ev/event (
Kevin McNee - google account
2016/11/07 23:08:27
Done.
| |
64 // We must preventDefault if there is a two finger touch. By doing so | |
65 // native pinch-zoom does not interfere with our way of handling the event. | |
66 if (ev.touches.length == 2) { | |
67 ev.preventDefault(); | |
68 this.pinchStartEvent_ = ev; | |
69 this.lastEvent_ = ev; | |
70 this.notify_({ | |
71 type: 'pinchstart', | |
72 center: GestureDetector.center_(ev) | |
73 }); | |
74 } | |
75 } | |
76 | |
77 /** | |
78 * The callback for touch move, end, and cancel events on the element. | |
79 * @private | |
80 * @param {!TouchEvent} ev Touch event on the element. | |
81 */ | |
82 onTouch_(ev) { | |
83 if (!this.pinchStartEvent_) | |
84 return; | |
85 | |
86 // Check if the pinch ends with the current event. | |
87 if (ev.touches.length < 2 || | |
88 this.lastEvent_.touches.length !== ev.touches.length) { | |
89 var startScaleRatio = GestureDetector.pinchScaleRatio_( | |
90 this.lastEvent_, this.pinchStartEvent_); | |
91 var center = GestureDetector.center_(this.lastEvent_); | |
92 var endEvent = { | |
93 type: 'pinchend', | |
94 startScaleRatio: startScaleRatio, | |
95 center: center | |
96 }; | |
97 this.pinchStartEvent_ = null; | |
98 this.lastEvent_ = null; | |
99 this.notify_(endEvent); | |
100 return; | |
101 } | |
102 | |
103 var scaleRatio = GestureDetector.pinchScaleRatio_(ev, this.lastEvent_); | |
104 var startScaleRatio = GestureDetector.pinchScaleRatio_( | |
105 ev, this.pinchStartEvent_); | |
106 var center = GestureDetector.center_(ev); | |
107 this.notify_({ | |
108 type: 'pinchupdate', | |
109 scaleRatio: scaleRatio, | |
110 direction: scaleRatio > 1.0 ? 'in' : 'out', | |
111 startScaleRatio: startScaleRatio, | |
112 center: center | |
113 }); | |
114 | |
115 this.lastEvent_ = ev; | |
116 } | |
117 | |
118 /** | |
119 * Computes the change in scale between this touch event | |
120 * and a previous one. | |
121 * @private | |
122 * @param {!TouchEvent} ev Latest touch event on the element. | |
123 * @param {!TouchEvent} prevEv A previous touch event on the element. | |
124 */ | |
125 static pinchScaleRatio_(ev, prevEv) { | |
126 var distance1 = GestureDetector.distance_(prevEv); | |
127 var distance2 = GestureDetector.distance_(ev); | |
128 if (distance1 === 0) | |
dpapad
2016/10/31 23:02:23
return distance1 === 0 ? null : distance2 / distan
Kevin McNee - google account
2016/11/07 23:08:27
Done.
| |
129 return null; | |
130 return distance2 / distance1; | |
131 } | |
132 | |
133 /** | |
134 * Computes the distance between fingers. | |
135 * @private | |
136 * @param {!TouchEvent} ev Touch event with at least 2 touch points. | |
137 * @return {number} Distance between touch[0] and touch[1]. | |
138 */ | |
139 static distance_(ev) { | |
140 var touch1 = ev.touches[0]; | |
141 var touch2 = ev.touches[1]; | |
142 var dx = touch1.clientX - touch2.clientX; | |
143 var dy = touch1.clientY - touch2.clientY; | |
144 return Math.sqrt(dx * dx + dy * dy); | |
145 } | |
146 | |
147 /** | |
148 * Computes the midpoint between fingers. | |
149 * @private | |
150 * @param {!TouchEvent} ev Touch event with at least 2 touch points. | |
151 * @return {Object} Midpoint between touch[0] and touch[1]. | |
152 */ | |
153 static center_(ev) { | |
154 var touch1 = ev.touches[0]; | |
155 var touch2 = ev.touches[1]; | |
156 return { | |
157 x: (touch1.clientX + touch2.clientX) / 2, | |
158 y: (touch1.clientY + touch2.clientY) / 2 | |
159 }; | |
160 } | |
161 }; | |
OLD | NEW |