From 944d2595af5435dd8efa38ed1e54b9ce504a195b Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 7 Aug 2014 12:22:26 -0500 Subject: [PATCH] fix(sideMenu): fix stopping content scrolling When a list was within a side menu it could scroll up and down, but if the user happened to drag a little bit on the X axis, then it would try to open the side menu and the Y scroll of the content stopped. Closes #1541 --- js/angular/directive/sideMenuContent.js | 109 ++++++++++++++++-------- js/utils/tap.js | 38 ++++----- js/views/scrollView.js | 4 +- test/unit/utils/tap.unit.js | 16 ++-- 4 files changed, 101 insertions(+), 66 deletions(-) diff --git a/js/angular/directive/sideMenuContent.js b/js/angular/directive/sideMenuContent.js index 3bdccf64cac..04afb59ada9 100644 --- a/js/angular/directive/sideMenuContent.js +++ b/js/angular/directive/sideMenuContent.js @@ -39,6 +39,8 @@ function($timeout, $ionicGesture) { compile: function(element, attr) { return { pre: prelink }; function prelink($scope, $element, $attr, sideMenuCtrl) { + var startCoord = null; + var primaryScrollAxis = null; $element.addClass('menu-content pane'); @@ -54,48 +56,75 @@ function($timeout, $ionicGesture) { $scope.$watch(attr.edgeDragThreshold, function(value) { sideMenuCtrl.edgeDragThreshold(value); }); - } - - var defaultPrevented = false; - var isDragging = false; + } // Listen for taps on the content to close the menu - function contentTap(e) { + function onContentTap(e) { if(sideMenuCtrl.getOpenAmount() !== 0) { sideMenuCtrl.close(); e.gesture.srcEvent.preventDefault(); + startCoord = null; + primaryScrollAxis = null; } } - ionic.on('tap', contentTap, $element[0]); - var dragFn = function(e) { - if(defaultPrevented || !sideMenuCtrl.isDraggableTarget(e)) return; - isDragging = true; - sideMenuCtrl._handleDrag(e); - e.gesture.srcEvent.preventDefault(); - }; + function onDragX(e) { + if(!sideMenuCtrl.isDraggableTarget(e)) return; + + if( getPrimaryScrollAxis(e) == 'x') { + sideMenuCtrl._handleDrag(e); + e.gesture.srcEvent.preventDefault(); + } + } - var dragVertFn = function(e) { - if(isDragging) { + function onDragY(e) { + if( getPrimaryScrollAxis(e) == 'x' ) { e.gesture.srcEvent.preventDefault(); } - }; - - //var dragGesture = Gesture.on('drag', dragFn, $element); - var dragRightGesture = $ionicGesture.on('dragright', dragFn, $element); - var dragLeftGesture = $ionicGesture.on('dragleft', dragFn, $element); - var dragUpGesture = $ionicGesture.on('dragup', dragVertFn, $element); - var dragDownGesture = $ionicGesture.on('dragdown', dragVertFn, $element); - - var dragReleaseFn = function(e) { - isDragging = false; - if(!defaultPrevented) { - sideMenuCtrl._endDrag(e); + } + + function onDragRelease(e) { + sideMenuCtrl._endDrag(e); + startCoord = null; + primaryScrollAxis = null; + } + + function getPrimaryScrollAxis(gestureEvt) { + // gets whether the user is primarily scrolling on the X or Y + // If a majority of the drag has been on the Y since the start of + // the drag, but the X has moved a little bit, it's still a Y drag + + if(primaryScrollAxis) { + // we already figured out which way they're scrolling + return primaryScrollAxis } - defaultPrevented = false; - }; - var releaseGesture = $ionicGesture.on('release', dragReleaseFn, $element); + if(gestureEvt && gestureEvt.gesture) { + + if(!startCoord) { + // get the starting point + startCoord = ionic.tap.pointerCoord(gestureEvt.gesture.srcEvent); + + } else { + // we already have a starting point, figure out which direction they're going + var endCoord = ionic.tap.pointerCoord(gestureEvt.gesture.srcEvent); + + var xDistance = Math.abs(endCoord.x - startCoord.x); + var yDistance = Math.abs(endCoord.y - startCoord.y); + + var scrollAxis = ( xDistance > yDistance ? 'x' : 'y' ); + + if( Math.max(xDistance, yDistance) > 30 ) { + // ok, we pretty much know which way they're going + // let's lock it in + primaryScrollAxis = scrollAxis; + } + + return scrollAxis; + } + + } + } sideMenuCtrl.setContent({ element: element[0], @@ -111,25 +140,31 @@ function($timeout, $ionicGesture) { }); }), enableAnimation: function() { - //this.el.classList.add(this.animateClass); $scope.animationEnabled = true; $element[0].classList.add('menu-animated'); }, disableAnimation: function() { - //this.el.classList.remove(this.animateClass); $scope.animationEnabled = false; $element[0].classList.remove('menu-animated'); } }); + // add gesture handlers + var dragRightGesture = $ionicGesture.on('dragright', onDragX, $element); + var dragLeftGesture = $ionicGesture.on('dragleft', onDragX, $element); + var dragUpGesture = $ionicGesture.on('dragup', onDragY, $element); + var dragDownGesture = $ionicGesture.on('dragdown', onDragY, $element); + var releaseGesture = $ionicGesture.on('release', onDragRelease, $element); + var contentTapGesture = $ionicGesture.on('tap', onContentTap, $element); + // Cleanup $scope.$on('$destroy', function() { - $ionicGesture.off(dragLeftGesture, 'dragleft', dragFn); - $ionicGesture.off(dragRightGesture, 'dragright', dragFn); - $ionicGesture.off(dragUpGesture, 'dragup', dragFn); - $ionicGesture.off(dragDownGesture, 'dragdown', dragFn); - $ionicGesture.off(releaseGesture, 'release', dragReleaseFn); - ionic.off('tap', contentTap, $element[0]); + $ionicGesture.off(dragLeftGesture, 'dragleft', onDragX); + $ionicGesture.off(dragRightGesture, 'dragright', onDragX); + $ionicGesture.off(dragUpGesture, 'dragup', onDragY); + $ionicGesture.off(dragDownGesture, 'dragdown', onDragY); + $ionicGesture.off(releaseGesture, 'release', onDragRelease); + $ionicGesture.off(contentTapGesture, 'tap', onContentTap); }); } } diff --git a/js/utils/tap.js b/js/utils/tap.js index f150435d584..68a3b46d304 100644 --- a/js/utils/tap.js +++ b/js/utils/tap.js @@ -266,6 +266,21 @@ ionic.tap = { // used to cancel any simulated clicks which may happen on a touchend/mouseup // gestures uses this method within its tap and hold events tapPointerMoved = true; + }, + + pointerCoord: function(event) { + // This method can get coordinates for both a mouse click + // or a touch depending on the given event + var c = { x:0, y:0 }; + if(event) { + var touches = event.touches && event.touches.length ? event.touches : [event]; + var e = (event.changedTouches && event.changedTouches[0]) || touches[0]; + if(e) { + c.x = e.clientX || e.pageX || 0; + c.y = e.clientY || e.pageY || 0; + } + } + return c; } }; @@ -285,7 +300,7 @@ function tapClick(e) { if( ionic.tap.requiresNativeClick(ele) || tapPointerMoved ) return false; - var c = getPointerCoordinates(e); + var c = ionic.tap.pointerCoord(e); console.log('tapClick', e.type, ele.tagName, '('+c.x+','+c.y+')'); triggerMouseEvent('click', ele, c.x, c.y); @@ -342,7 +357,7 @@ function tapMouseDown(e) { } tapPointerMoved = false; - tapPointerStart = getPointerCoordinates(e); + tapPointerStart = ionic.tap.pointerCoord(e); tapEventListener('mousemove'); ionic.activator.start(e); @@ -382,7 +397,7 @@ function tapTouchStart(e) { tapPointerMoved = false; tapEnableTouchEvents(); - tapPointerStart = getPointerCoordinates(e); + tapPointerStart = ionic.tap.pointerCoord(e); tapEventListener(tapTouchMoveListener); ionic.activator.start(e); @@ -530,7 +545,7 @@ function tapHasPointerMoved(endEvent) { if(!endEvent || endEvent.target.nodeType !== 1 || !tapPointerStart || ( tapPointerStart.x === 0 && tapPointerStart.y === 0 )) { return false; } - var endCoordinates = getPointerCoordinates(endEvent); + var endCoordinates = ionic.tap.pointerCoord(endEvent); var hasClassList = !!(endEvent.target.classList && endEvent.target.classList.contains); var releaseTolerance = hasClassList & endEvent.target.classList.contains('button') ? @@ -541,21 +556,6 @@ function tapHasPointerMoved(endEvent) { Math.abs(tapPointerStart.y - endCoordinates.y) > releaseTolerance; } -function getPointerCoordinates(event) { - // This method can get coordinates for both a mouse click - // or a touch depending on the given event - var c = { x:0, y:0 }; - if(event) { - var touches = event.touches && event.touches.length ? event.touches : [event]; - var e = (event.changedTouches && event.changedTouches[0]) || touches[0]; - if(e) { - c.x = e.clientX || e.pageX || 0; - c.y = e.clientY || e.pageY || 0; - } - } - return c; -} - function tapContainingElement(ele, allowSelf) { var climbEle = ele; for(var x=0; x<6; x++) { diff --git a/js/views/scrollView.js b/js/views/scrollView.js index dc72bc5c28c..94b8db257d8 100644 --- a/js/views/scrollView.js +++ b/js/views/scrollView.js @@ -704,7 +704,7 @@ ionic.views.Scroll = ionic.views.View.inherit({ } self.touchStart = function(e) { - self.startCoordinates = getPointerCoordinates(e); + self.startCoordinates = ionic.tap.pointerCoord(e); if ( ionic.tap.ignoreScrollStart(e) ) { return; @@ -744,7 +744,7 @@ ionic.views.Scroll = ionic.views.View.inherit({ if(self.startCoordinates) { // we have start coordinates, so get this touch move's current coordinates - var currentCoordinates = getPointerCoordinates(e); + var currentCoordinates = ionic.tap.pointerCoord(e); if( self.__isSelectable && ionic.tap.isTextInput(e.target) && diff --git a/test/unit/utils/tap.unit.js b/test/unit/utils/tap.unit.js index 3335f98f22b..bae9ba24c2e 100644 --- a/test/unit/utils/tap.unit.js +++ b/test/unit/utils/tap.unit.js @@ -619,13 +619,13 @@ describe('Ionic Tap', function() { it('Should get coordinates from page mouse event', function() { var e = { pageX: 77, pageY: 77 }; - var c = getPointerCoordinates(e); + var c = ionic.tap.pointerCoord(e); expect(c).toEqual({x:77, y: 77}); }); it('Should get coordinates from client mouse event', function() { var e = { clientX: 77, clientY: 77 }; - var c = getPointerCoordinates(e); + var c = ionic.tap.pointerCoord(e); expect(c).toEqual({x:77, y: 77}); }); @@ -634,7 +634,7 @@ describe('Ionic Tap', function() { touches: [{ clientX: 99, clientY: 99 }], changedTouches: [{ clientX: 88, clientY: 88 }] }; - var c = getPointerCoordinates(e); + var c = ionic.tap.pointerCoord(e); expect(c).toEqual({x:88, y: 88}); }); @@ -642,7 +642,7 @@ describe('Ionic Tap', function() { var e = { touches: [{ pageX: 99, pageY: 99 }] }; - var c = getPointerCoordinates(e); + var c = ionic.tap.pointerCoord(e); expect(c).toEqual({x:99, y: 99}); }); @@ -650,13 +650,13 @@ describe('Ionic Tap', function() { var e = { touches: [{ clientX: 99, clientY: 99 }] }; - var c = getPointerCoordinates(e); + var c = ionic.tap.pointerCoord(e); expect(c).toEqual({x:99, y: 99}); }); it('Should get 0 coordinates', function() { var e = {}; - var c = getPointerCoordinates(e); + var c = ionic.tap.pointerCoord(e); expect(c).toEqual({x:0, y: 0}); }); @@ -1232,8 +1232,8 @@ describe('Ionic Tap', function() { ele.type = 'text'; expect( ionic.tap.isDateInput(ele) ).toEqual(false); - - + + }); it('Should isLabelWithTextInput', function() {