1 |
|
2 |
|
3 |
|
4 | import { G as GESTURE_CONTROLLER } from './gesture-controller.js';
|
5 | export { G as GESTURE_CONTROLLER } from './gesture-controller.js';
|
6 |
|
7 | const addEventListener = (el, eventName, callback, opts) => {
|
8 |
|
9 |
|
10 | const listenerOpts = supportsPassive(el) ? {
|
11 | 'capture': !!opts.capture,
|
12 | 'passive': !!opts.passive,
|
13 | } : !!opts.capture;
|
14 | let add;
|
15 | let remove;
|
16 | if (el['__zone_symbol__addEventListener']) {
|
17 | add = '__zone_symbol__addEventListener';
|
18 | remove = '__zone_symbol__removeEventListener';
|
19 | }
|
20 | else {
|
21 | add = 'addEventListener';
|
22 | remove = 'removeEventListener';
|
23 | }
|
24 | el[add](eventName, callback, listenerOpts);
|
25 | return () => {
|
26 | el[remove](eventName, callback, listenerOpts);
|
27 | };
|
28 | };
|
29 | const supportsPassive = (node) => {
|
30 | if (_sPassive === undefined) {
|
31 | try {
|
32 | const opts = Object.defineProperty({}, 'passive', {
|
33 | get: () => {
|
34 | _sPassive = true;
|
35 | }
|
36 | });
|
37 | node.addEventListener('optsTest', () => { return; }, opts);
|
38 | }
|
39 | catch (e) {
|
40 | _sPassive = false;
|
41 | }
|
42 | }
|
43 | return !!_sPassive;
|
44 | };
|
45 | let _sPassive;
|
46 |
|
47 | const MOUSE_WAIT = 2000;
|
48 | const createPointerEvents = (el, pointerDown, pointerMove, pointerUp, options) => {
|
49 | let rmTouchStart;
|
50 | let rmTouchMove;
|
51 | let rmTouchEnd;
|
52 | let rmTouchCancel;
|
53 | let rmMouseStart;
|
54 | let rmMouseMove;
|
55 | let rmMouseUp;
|
56 | let lastTouchEvent = 0;
|
57 | const handleTouchStart = (ev) => {
|
58 | lastTouchEvent = Date.now() + MOUSE_WAIT;
|
59 | if (!pointerDown(ev)) {
|
60 | return;
|
61 | }
|
62 | if (!rmTouchMove && pointerMove) {
|
63 | rmTouchMove = addEventListener(el, 'touchmove', pointerMove, options);
|
64 | }
|
65 | |
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | if (!rmTouchEnd) {
|
75 | rmTouchEnd = addEventListener(ev.target, 'touchend', handleTouchEnd, options);
|
76 | }
|
77 | if (!rmTouchCancel) {
|
78 | rmTouchCancel = addEventListener(ev.target, 'touchcancel', handleTouchEnd, options);
|
79 | }
|
80 | };
|
81 | const handleMouseDown = (ev) => {
|
82 | if (lastTouchEvent > Date.now()) {
|
83 | return;
|
84 | }
|
85 | if (!pointerDown(ev)) {
|
86 | return;
|
87 | }
|
88 | if (!rmMouseMove && pointerMove) {
|
89 | rmMouseMove = addEventListener(getDocument(el), 'mousemove', pointerMove, options);
|
90 | }
|
91 | if (!rmMouseUp) {
|
92 | rmMouseUp = addEventListener(getDocument(el), 'mouseup', handleMouseUp, options);
|
93 | }
|
94 | };
|
95 | const handleTouchEnd = (ev) => {
|
96 | stopTouch();
|
97 | if (pointerUp) {
|
98 | pointerUp(ev);
|
99 | }
|
100 | };
|
101 | const handleMouseUp = (ev) => {
|
102 | stopMouse();
|
103 | if (pointerUp) {
|
104 | pointerUp(ev);
|
105 | }
|
106 | };
|
107 | const stopTouch = () => {
|
108 | if (rmTouchMove) {
|
109 | rmTouchMove();
|
110 | }
|
111 | if (rmTouchEnd) {
|
112 | rmTouchEnd();
|
113 | }
|
114 | if (rmTouchCancel) {
|
115 | rmTouchCancel();
|
116 | }
|
117 | rmTouchMove = rmTouchEnd = rmTouchCancel = undefined;
|
118 | };
|
119 | const stopMouse = () => {
|
120 | if (rmMouseMove) {
|
121 | rmMouseMove();
|
122 | }
|
123 | if (rmMouseUp) {
|
124 | rmMouseUp();
|
125 | }
|
126 | rmMouseMove = rmMouseUp = undefined;
|
127 | };
|
128 | const stop = () => {
|
129 | stopTouch();
|
130 | stopMouse();
|
131 | };
|
132 | const enable = (isEnabled = true) => {
|
133 | if (!isEnabled) {
|
134 | if (rmTouchStart) {
|
135 | rmTouchStart();
|
136 | }
|
137 | if (rmMouseStart) {
|
138 | rmMouseStart();
|
139 | }
|
140 | rmTouchStart = rmMouseStart = undefined;
|
141 | stop();
|
142 | }
|
143 | else {
|
144 | if (!rmTouchStart) {
|
145 | rmTouchStart = addEventListener(el, 'touchstart', handleTouchStart, options);
|
146 | }
|
147 | if (!rmMouseStart) {
|
148 | rmMouseStart = addEventListener(el, 'mousedown', handleMouseDown, options);
|
149 | }
|
150 | }
|
151 | };
|
152 | const destroy = () => {
|
153 | enable(false);
|
154 | pointerUp = pointerMove = pointerDown = undefined;
|
155 | };
|
156 | return {
|
157 | enable,
|
158 | stop,
|
159 | destroy
|
160 | };
|
161 | };
|
162 | const getDocument = (node) => {
|
163 | return node instanceof Document ? node : node.ownerDocument;
|
164 | };
|
165 |
|
166 | const createPanRecognizer = (direction, thresh, maxAngle) => {
|
167 | const radians = maxAngle * (Math.PI / 180);
|
168 | const isDirX = direction === 'x';
|
169 | const maxCosine = Math.cos(radians);
|
170 | const threshold = thresh * thresh;
|
171 | let startX = 0;
|
172 | let startY = 0;
|
173 | let dirty = false;
|
174 | let isPan = 0;
|
175 | return {
|
176 | start(x, y) {
|
177 | startX = x;
|
178 | startY = y;
|
179 | isPan = 0;
|
180 | dirty = true;
|
181 | },
|
182 | detect(x, y) {
|
183 | if (!dirty) {
|
184 | return false;
|
185 | }
|
186 | const deltaX = (x - startX);
|
187 | const deltaY = (y - startY);
|
188 | const distance = deltaX * deltaX + deltaY * deltaY;
|
189 | if (distance < threshold) {
|
190 | return false;
|
191 | }
|
192 | const hypotenuse = Math.sqrt(distance);
|
193 | const cosine = (isDirX ? deltaX : deltaY) / hypotenuse;
|
194 | if (cosine > maxCosine) {
|
195 | isPan = 1;
|
196 | }
|
197 | else if (cosine < -maxCosine) {
|
198 | isPan = -1;
|
199 | }
|
200 | else {
|
201 | isPan = 0;
|
202 | }
|
203 | dirty = false;
|
204 | return true;
|
205 | },
|
206 | isGesture() {
|
207 | return isPan !== 0;
|
208 | },
|
209 | getDirection() {
|
210 | return isPan;
|
211 | }
|
212 | };
|
213 | };
|
214 |
|
215 | const createGesture = (config) => {
|
216 | let hasCapturedPan = false;
|
217 | let hasStartedPan = false;
|
218 | let hasFiredStart = true;
|
219 | let isMoveQueued = false;
|
220 | const finalConfig = Object.assign({ disableScroll: false, direction: 'x', gesturePriority: 0, passive: true, maxAngle: 40, threshold: 10 }, config);
|
221 | const canStart = finalConfig.canStart;
|
222 | const onWillStart = finalConfig.onWillStart;
|
223 | const onStart = finalConfig.onStart;
|
224 | const onEnd = finalConfig.onEnd;
|
225 | const notCaptured = finalConfig.notCaptured;
|
226 | const onMove = finalConfig.onMove;
|
227 | const threshold = finalConfig.threshold;
|
228 | const passive = finalConfig.passive;
|
229 | const blurOnStart = finalConfig.blurOnStart;
|
230 | const detail = {
|
231 | type: 'pan',
|
232 | startX: 0,
|
233 | startY: 0,
|
234 | startTime: 0,
|
235 | currentX: 0,
|
236 | currentY: 0,
|
237 | velocityX: 0,
|
238 | velocityY: 0,
|
239 | deltaX: 0,
|
240 | deltaY: 0,
|
241 | currentTime: 0,
|
242 | event: undefined,
|
243 | data: undefined
|
244 | };
|
245 | const pan = createPanRecognizer(finalConfig.direction, finalConfig.threshold, finalConfig.maxAngle);
|
246 | const gesture = GESTURE_CONTROLLER.createGesture({
|
247 | name: config.gestureName,
|
248 | priority: config.gesturePriority,
|
249 | disableScroll: config.disableScroll
|
250 | });
|
251 | const pointerDown = (ev) => {
|
252 | const timeStamp = now(ev);
|
253 | if (hasStartedPan || !hasFiredStart) {
|
254 | return false;
|
255 | }
|
256 | updateDetail(ev, detail);
|
257 | detail.startX = detail.currentX;
|
258 | detail.startY = detail.currentY;
|
259 | detail.startTime = detail.currentTime = timeStamp;
|
260 | detail.velocityX = detail.velocityY = detail.deltaX = detail.deltaY = 0;
|
261 | detail.event = ev;
|
262 |
|
263 | if (canStart && canStart(detail) === false) {
|
264 | return false;
|
265 | }
|
266 |
|
267 | gesture.release();
|
268 |
|
269 | if (!gesture.start()) {
|
270 | return false;
|
271 | }
|
272 | hasStartedPan = true;
|
273 | if (threshold === 0) {
|
274 | return tryToCapturePan();
|
275 | }
|
276 | pan.start(detail.startX, detail.startY);
|
277 | return true;
|
278 | };
|
279 | const pointerMove = (ev) => {
|
280 |
|
281 |
|
282 | if (hasCapturedPan) {
|
283 | if (!isMoveQueued && hasFiredStart) {
|
284 | isMoveQueued = true;
|
285 | calcGestureData(detail, ev);
|
286 | requestAnimationFrame(fireOnMove);
|
287 | }
|
288 | return;
|
289 | }
|
290 |
|
291 | calcGestureData(detail, ev);
|
292 | if (pan.detect(detail.currentX, detail.currentY)) {
|
293 | if (!pan.isGesture() || !tryToCapturePan()) {
|
294 | abortGesture();
|
295 | }
|
296 | }
|
297 | };
|
298 | const fireOnMove = () => {
|
299 |
|
300 |
|
301 | if (!hasCapturedPan) {
|
302 | return;
|
303 | }
|
304 | isMoveQueued = false;
|
305 | if (onMove) {
|
306 | onMove(detail);
|
307 | }
|
308 | };
|
309 | const tryToCapturePan = () => {
|
310 | if (gesture && !gesture.capture()) {
|
311 | return false;
|
312 | }
|
313 | hasCapturedPan = true;
|
314 | hasFiredStart = false;
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 | detail.startX = detail.currentX;
|
322 | detail.startY = detail.currentY;
|
323 | detail.startTime = detail.currentTime;
|
324 | if (onWillStart) {
|
325 | onWillStart(detail).then(fireOnStart);
|
326 | }
|
327 | else {
|
328 | fireOnStart();
|
329 | }
|
330 | return true;
|
331 | };
|
332 | const blurActiveElement = () => {
|
333 |
|
334 | if (typeof document !== 'undefined') {
|
335 | const activeElement = document.activeElement;
|
336 | if (activeElement !== null && activeElement.blur) {
|
337 | activeElement.blur();
|
338 | }
|
339 | }
|
340 | };
|
341 | const fireOnStart = () => {
|
342 | if (blurOnStart) {
|
343 | blurActiveElement();
|
344 | }
|
345 | if (onStart) {
|
346 | onStart(detail);
|
347 | }
|
348 | hasFiredStart = true;
|
349 | };
|
350 | const reset = () => {
|
351 | hasCapturedPan = false;
|
352 | hasStartedPan = false;
|
353 | isMoveQueued = false;
|
354 | hasFiredStart = true;
|
355 | gesture.release();
|
356 | };
|
357 |
|
358 | const pointerUp = (ev) => {
|
359 | const tmpHasCaptured = hasCapturedPan;
|
360 | const tmpHasFiredStart = hasFiredStart;
|
361 | reset();
|
362 | if (!tmpHasFiredStart) {
|
363 | return;
|
364 | }
|
365 | calcGestureData(detail, ev);
|
366 |
|
367 | if (tmpHasCaptured) {
|
368 | if (onEnd) {
|
369 | onEnd(detail);
|
370 | }
|
371 | return;
|
372 | }
|
373 |
|
374 | if (notCaptured) {
|
375 | notCaptured(detail);
|
376 | }
|
377 | };
|
378 | const pointerEvents = createPointerEvents(finalConfig.el, pointerDown, pointerMove, pointerUp, {
|
379 | capture: false,
|
380 | passive
|
381 | });
|
382 | const abortGesture = () => {
|
383 | reset();
|
384 | pointerEvents.stop();
|
385 | if (notCaptured) {
|
386 | notCaptured(detail);
|
387 | }
|
388 | };
|
389 | return {
|
390 | enable(enable = true) {
|
391 | if (!enable) {
|
392 | if (hasCapturedPan) {
|
393 | pointerUp(undefined);
|
394 | }
|
395 | reset();
|
396 | }
|
397 | pointerEvents.enable(enable);
|
398 | },
|
399 | destroy() {
|
400 | gesture.destroy();
|
401 | pointerEvents.destroy();
|
402 | }
|
403 | };
|
404 | };
|
405 | const calcGestureData = (detail, ev) => {
|
406 | if (!ev) {
|
407 | return;
|
408 | }
|
409 | const prevX = detail.currentX;
|
410 | const prevY = detail.currentY;
|
411 | const prevT = detail.currentTime;
|
412 | updateDetail(ev, detail);
|
413 | const currentX = detail.currentX;
|
414 | const currentY = detail.currentY;
|
415 | const timestamp = detail.currentTime = now(ev);
|
416 | const timeDelta = timestamp - prevT;
|
417 | if (timeDelta > 0 && timeDelta < 100) {
|
418 | const velocityX = (currentX - prevX) / timeDelta;
|
419 | const velocityY = (currentY - prevY) / timeDelta;
|
420 | detail.velocityX = velocityX * 0.7 + detail.velocityX * 0.3;
|
421 | detail.velocityY = velocityY * 0.7 + detail.velocityY * 0.3;
|
422 | }
|
423 | detail.deltaX = currentX - detail.startX;
|
424 | detail.deltaY = currentY - detail.startY;
|
425 | detail.event = ev;
|
426 | };
|
427 | const updateDetail = (ev, detail) => {
|
428 |
|
429 |
|
430 | let x = 0;
|
431 | let y = 0;
|
432 | if (ev) {
|
433 | const changedTouches = ev.changedTouches;
|
434 | if (changedTouches && changedTouches.length > 0) {
|
435 | const touch = changedTouches[0];
|
436 | x = touch.clientX;
|
437 | y = touch.clientY;
|
438 | }
|
439 | else if (ev.pageX !== undefined) {
|
440 | x = ev.pageX;
|
441 | y = ev.pageY;
|
442 | }
|
443 | }
|
444 | detail.currentX = x;
|
445 | detail.currentY = y;
|
446 | };
|
447 | const now = (ev) => {
|
448 | return ev.timeStamp || Date.now();
|
449 | };
|
450 |
|
451 | export { createGesture };
|