Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit d3cb371

Browse files
devversionThomasBurleson
authored andcommitted
fix(gestures): detect touch action and provide polyfill.
* On non-android browsers, touchmove events were cancelled and native scroll was disabled as well. * touchAction is supported by the most modern-browsers and drops the cancellation of the touchmove event * If touchAction is not supported, then we're able to use the polyfill (cancelling the touchMove event) Fixes #7311. Closes #7857
1 parent 0c34ff4 commit d3cb371

File tree

1 file changed

+37
-5
lines changed

1 file changed

+37
-5
lines changed

src/core/services/gesture/gesture.js

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
7373
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
7474
var isIos = userAgent.match(/ipad|iphone|ipod/i);
7575
var isAndroid = userAgent.match(/android/i);
76+
var touchActionProperty = getTouchAction();
7677
var hasJQuery = (typeof window.jQuery !== 'undefined') && (angular.element === window.jQuery);
7778

7879
var self = {
@@ -215,7 +216,7 @@ function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
215216
// If we don't preventDefault touchmove events here, Android will assume we don't
216217
// want to listen to anymore touch events. It will start scrolling and stop sending
217218
// touchmove events.
218-
ev.preventDefault();
219+
if (!touchActionProperty && ev.type === 'touchmove') ev.preventDefault();
219220

220221
// If the user moves greater than <maxDistance> pixels, stop the hold timer
221222
// set in onStart
@@ -234,7 +235,7 @@ function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
234235
* The drag handler dispatches a drag event if the user holds and moves his finger greater than
235236
* <minDistance> px in the x or y direction, depending on options.horizontal.
236237
* The drag will be cancelled if the user moves his finger greater than <minDistance>*<cancelMultiplier> in
237-
* the perpindicular direction. Eg if the drag is horizontal and the user moves his finger <minDistance>*<cancelMultiplier>
238+
* the perpendicular direction. Eg if the drag is horizontal and the user moves his finger <minDistance>*<cancelMultiplier>
238239
* pixels vertically, this handler won't consider the move part of a drag.
239240
*/
240241
.handler('drag', {
@@ -243,6 +244,18 @@ function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
243244
horizontal: true,
244245
cancelMultiplier: 1.5
245246
},
247+
onSetup: function(element, options) {
248+
if (touchActionProperty) {
249+
// We check for horizontal to be false, because otherwise we would overwrite the default opts.
250+
this.oldTouchAction = element[0].style[touchActionProperty];
251+
element[0].style[touchActionProperty] = options.horizontal === false ? 'pan-y' : 'pan-x';
252+
}
253+
},
254+
onCleanup: function(element) {
255+
if (this.oldTouchAction) {
256+
element[0].style[touchActionProperty] = this.oldTouchAction;
257+
}
258+
},
246259
onStart: function (ev) {
247260
// For drag, require a parent to be registered with $mdGesture.register()
248261
if (!this.state.registeredParent) this.cancel();
@@ -253,7 +266,7 @@ function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
253266
// If we don't preventDefault touchmove events here, Android will assume we don't
254267
// want to listen to anymore touch events. It will start scrolling and stop sending
255268
// touchmove events.
256-
ev.preventDefault();
269+
if (!touchActionProperty && ev.type === 'touchmove') ev.preventDefault();
257270

258271
if (!this.state.dragPointer) {
259272
if (this.state.options.horizontal) {
@@ -277,7 +290,7 @@ function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
277290
this.dispatchDragMove(ev);
278291
}
279292
},
280-
// Only dispatch dragmove events every frame; any more is unnecessray
293+
// Only dispatch dragmove events every frame; any more is unnecessary
281294
dispatchDragMove: $$rAF.throttle(function (ev) {
282295
// Make sure the drag didn't stop while waiting for the next frame
283296
if (this.state.isRunning) {
@@ -318,6 +331,19 @@ function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
318331
}
319332
});
320333

334+
function getTouchAction() {
335+
var testEl = document.createElement('div');
336+
var vendorPrefixes = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
337+
338+
for (var i = 0; i < vendorPrefixes.length; i++) {
339+
var prefix = vendorPrefixes[i];
340+
var property = prefix ? prefix + 'TouchAction' : 'touchAction';
341+
if (angular.isDefined(testEl.style[property])) {
342+
return property;
343+
}
344+
}
345+
}
346+
321347
}
322348

323349
/**
@@ -346,6 +372,8 @@ function MdGestureHandler() {
346372
dispatchEvent: hasJQuery ? jQueryDispatchEvent : nativeDispatchEvent,
347373

348374
// These are overridden by the registered handler
375+
onSetup: angular.noop,
376+
onCleanup: angular.noop,
349377
onStart: angular.noop,
350378
onMove: angular.noop,
351379
onEnd: angular.noop,
@@ -395,7 +423,7 @@ function MdGestureHandler() {
395423
return null;
396424
},
397425

398-
// Called from $mdGesture.register when an element reigsters itself with a handler.
426+
// Called from $mdGesture.register when an element registers itself with a handler.
399427
// Store the options the user gave on the DOMElement itself. These options will
400428
// be retrieved with getNearestParent when the handler starts.
401429
registerElement: function (element, options) {
@@ -404,11 +432,15 @@ function MdGestureHandler() {
404432
element[0].$mdGesture[this.name] = options || {};
405433
element.on('$destroy', onDestroy);
406434

435+
self.onSetup(element, options || {});
436+
407437
return onDestroy;
408438

409439
function onDestroy() {
410440
delete element[0].$mdGesture[self.name];
411441
element.off('$destroy', onDestroy);
442+
443+
self.onCleanup(element, options || {});
412444
}
413445
}
414446
};

0 commit comments

Comments
 (0)