Skip to content

Commit 002cd13

Browse files
authored
Merge pull request #72 from yappbox/cyril/improve-gesture-detection
Improve gesture detection
2 parents 42137e8 + 708bed8 commit 002cd13

File tree

1 file changed

+39
-10
lines changed

1 file changed

+39
-10
lines changed

addon/components/scroll-view.js

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,24 @@ let waiter = buildWaiter('yapp-scroll-view:scrolling');
2323
const FIELD_REGEXP = /input|textarea|select/i;
2424
const MEASUREMENT_INTERVAL = 250;
2525
const MEASUREMENT_INTERVAL_WHILE_SCROLLING_OR_OFFSCREEN = 1000;
26+
const LONG_PRESS_DELAY = 500;
27+
const GHOST_CLICK_DELAY = 100;
28+
29+
const isIPhone = /iPhone|iPod|iPad/i.test(navigator.appVersion);
30+
31+
let timeoutID = 0;
32+
function addCaptureClick(domElement) {
33+
if(timeoutID) {
34+
clearTimeout(timeoutID);
35+
} else {
36+
domElement.addEventListener('click', captureClick, true);
37+
}
38+
let cancelCaptureClick = () => {
39+
timeoutID = 0;
40+
domElement.removeEventListener('click', captureClick, true);
41+
}
42+
timeoutID = setTimeout(cancelCaptureClick, GHOST_CLICK_DELAY)
43+
}
2644

2745
function debounce(func, wait, immediate) {
2846
var timeout;
@@ -93,6 +111,7 @@ class ScrollView extends Component {
93111
_appliedScrollTop;
94112
_shouldMeasureContent = undefined;
95113
_isScrolling = false;
114+
_touchStartTimeStamp = null;
96115
_lastIsScrolling = false;
97116

98117
@service('scroll-position-memory')
@@ -279,6 +298,7 @@ class ScrollView extends Component {
279298

280299
doTouchStart(touches, timeStamp) {
281300
this._wasScrollingAtTouchStart = this._isScrolling;
301+
this._touchStartTimeStamp = timeStamp;
282302
this.scroller.doTouchStart(touches, timeStamp);
283303
}
284304

@@ -287,37 +307,46 @@ class ScrollView extends Component {
287307
}
288308

289309
doTouchEnd(_touches, timeStamp, event) {
290-
let preventClick = this.needsPreventClick();
310+
let preventClick = this.needsPreventClick(timeStamp - this._touchStartTimeStamp);
291311

292312
if (preventClick) {
293313
// A touchend event can prevent a follow-on click event by calling preventDefault.
294-
// However, a mouseup event cannot do this so we need to capture the upcoming click instead.
295-
if (event instanceof MouseEvent) {
296-
this.scrollViewElement.addEventListener('click', captureClick, true);
297-
} else {
298-
event.preventDefault();
299-
event.stopPropagation();
314+
// On Android, it works well.
315+
// On iOS, we see a click event being triggered after a touchend event,
316+
// even when `preventDefault` and `stopPropagation` were called. However, phantom clicks
317+
// are not triggered consistently. In order to avoid capturing legit click events,
318+
// we only try to capture phantom clicks if they happen less than 100ms after a touchend event.
319+
// On desktop browsers, a mouseup event cannot do this so we need to capture the upcoming click instead.
320+
321+
if (isIPhone || (event instanceof MouseEvent)) {
322+
addCaptureClick(this.scrollViewElement);
300323
}
324+
event.preventDefault();
325+
event.stopPropagation();
301326
}
302327

328+
this._touchStartTimeStamp = null;
303329
this.scroller.doTouchEnd(timeStamp);
304330
}
305331

306-
needsPreventClick() {
307-
// There are two cases where we want to prevent the click that normally follows a mouseup/touchend.
332+
needsPreventClick(touchDuration) {
333+
// There are three cases where we want to prevent the click that normally follows a mouseup/touchend.
308334
//
309335
// 1) when the user is just finishing a purposeful scroll (i.e. dragging scroll view beyond a threshold)
336+
// This is only true on a desktop.
310337
// 2) when animating with "momentum", a tap should stop the movement rather than
311338
// trigger an interactive element that may be under the tap. Zynga scroller
312339
// takes care of stopping the movement, but we need to capture the click
313340
// and stop propagation.
341+
// 3) when the user does a long press (> 500 ms)
314342
//
315343
// This method determines whether either of these cases apply.
316344
let isFinishingDragging = this.scroller.__isDragging;
317345
let wasAnimatingWithMomentum =
318346
this._wasScrollingAtTouchStart &&
319347
Math.abs(this._decelerationVelocityY) > 2;
320-
return isFinishingDragging || wasAnimatingWithMomentum;
348+
let isLongPress = touchDuration > LONG_PRESS_DELAY;
349+
return isFinishingDragging || wasAnimatingWithMomentum || isLongPress;
321350
}
322351

323352
handleWheel(e) {

0 commit comments

Comments
 (0)