Browse Source

support for swipe and pinch gestures #48

Hakim El Hattab 12 years ago
parent
commit
4f08e2aeb0
2 changed files with 148 additions and 51 deletions
  1. 148 51
      js/reveal.js
  2. 0 0
      js/reveal.min.js

+ 148 - 51
js/reveal.js

@@ -53,7 +53,18 @@ var Reveal = (function(){
 		mouseWheelTimeout = 0,
 
 		// Delays updates to the URL due to a Chrome thumbnailer bug
-		writeURLTimeout = 0;
+		writeURLTimeout = 0,
+
+		// Holds information about the currently ongoing touch input
+		touch = {
+			startX: 0,
+			startY: 0,
+			startSpan: 0,
+			startCount: 0,
+			handled: false,
+			threshold: 40
+		};
+	
 	
 	/**
 	 * Starts up the slideshow by applying configuration
@@ -79,14 +90,7 @@ var Reveal = (function(){
 		dom.controlsUp = document.querySelector( '#reveal .controls .up' );
 		dom.controlsDown = document.querySelector( '#reveal .controls .down' );
 
-		// Bind all view events
-		document.addEventListener( 'keydown', onDocumentKeyDown, false );
-		document.addEventListener( 'touchstart', onDocumentTouchStart, false );
-		window.addEventListener( 'hashchange', onWindowHashChange, false );
-		dom.controlsLeft.addEventListener( 'click', preventAndForward( navigateLeft ), false );
-		dom.controlsRight.addEventListener( 'click', preventAndForward( navigateRight ), false );
-		dom.controlsUp.addEventListener( 'click', preventAndForward( navigateUp ), false );
-		dom.controlsDown.addEventListener( 'click', preventAndForward( navigateDown ), false );
+		addEventListeners();
 
 		// Copy options over to our config object
 		extend( config, options );
@@ -135,6 +139,33 @@ var Reveal = (function(){
 			window.addEventListener( 'load', removeAddressBar, false );
 			window.addEventListener( 'orientationchange', removeAddressBar, false );
 		}
+		
+	}
+
+	function addEventListeners() {
+		document.addEventListener( 'keydown', onDocumentKeyDown, false );
+		document.addEventListener( 'touchstart', onDocumentTouchStart, false );
+		document.addEventListener( 'touchmove', onDocumentTouchMove, false );
+		document.addEventListener( 'touchend', onDocumentTouchEnd, false );
+		window.addEventListener( 'hashchange', onWindowHashChange, false );
+		
+		dom.controlsLeft.addEventListener( 'click', preventAndForward( navigateLeft ), false );
+		dom.controlsRight.addEventListener( 'click', preventAndForward( navigateRight ), false );
+		dom.controlsUp.addEventListener( 'click', preventAndForward( navigateUp ), false );
+		dom.controlsDown.addEventListener( 'click', preventAndForward( navigateDown ), false );
+	}
+
+	function removeEventListeners() {
+		document.removeEventListener( 'keydown', onDocumentKeyDown, false );
+		document.removeEventListener( 'touchstart', onDocumentTouchStart, false );
+		document.removeEventListener( 'touchmove', onDocumentTouchMove, false );
+		document.removeEventListener( 'touchend', onDocumentTouchEnd, false );
+		window.removeEventListener( 'hashchange', onWindowHashChange, false );
+		
+		dom.controlsLeft.removeEventListener( 'click', preventAndForward( navigateLeft ), false );
+		dom.controlsRight.removeEventListener( 'click', preventAndForward( navigateRight ), false );
+		dom.controlsUp.removeEventListener( 'click', preventAndForward( navigateUp ), false );
+		dom.controlsDown.removeEventListener( 'click', preventAndForward( navigateDown ), false );
 	}
 
 	/**
@@ -147,6 +178,13 @@ var Reveal = (function(){
 		}
 	}
 
+	function distanceBetween( a, b ) {
+		var dx = a.x - b.x,
+			dy = a.y - b.y;
+
+		return Math.sqrt( dx*dx + dy*dy );
+	}
+
 	/**
 	 * Prevents an events defaults behavior calls the 
 	 * specified delegate.
@@ -224,54 +262,98 @@ var Reveal = (function(){
 		}
 
 	}
-	
+
 	/**
-	 * Handler for the document level 'touchstart' event.
-	 * 
-	 * This enables very basic tap interaction for touch
-	 * devices. Added mainly for performance testing of 3D
-	 * transforms on iOS but was so happily surprised with
-	 * how smoothly it runs so I left it in here. Apple +1
-	 * 
-	 * @param {Object} event
+	 * Handler for the document level 'touchstart' event,
+	 * enables support for swipe and pinch gestures.
 	 */
 	function onDocumentTouchStart( event ) {
-		// We're only interested in one point taps
-		if (event.touches.length === 1) {
-			// Never prevent taps on anchors and images
-			if( event.target.tagName.toLowerCase() === 'a' || event.target.tagName.toLowerCase() === 'img' ) {
-				return;
-			}
-			
-			event.preventDefault();
-			
-			var point = {
-				x: event.touches[0].clientX,
-				y: event.touches[0].clientY
-			};
-			
-			// Define the extent of the areas that may be tapped
-			// to navigate
-			var wt = window.innerWidth * 0.3;
-			var ht = window.innerHeight * 0.3;
-			
-			if( point.x < wt ) {
-				navigateLeft();
-			}
-			else if( point.x > window.innerWidth - wt ) {
-				navigateRight();
-			}
-			else if( point.y < ht ) {
-				navigateUp();
+		touch.startX = event.touches[0].clientX;
+		touch.startY = event.touches[0].clientY;
+		touch.startCount = event.touches.length;
+
+		// If there's two touches we need to memorize the distance 
+		// between those two points to detect pinching
+		if( event.touches.length === 2 ) {
+			touch.startSpan = distanceBetween( {
+				x: event.touches[1].clientX,
+				y: event.touches[1].clientY
+			}, {
+				x: touch.startX,
+				y: touch.startY
+			} );
+		}
+	}
+	
+	/**
+	 * Handler for the document level 'touchmove' event.
+	 */
+	function onDocumentTouchMove( event ) {
+		// Each touch should only trigger one action
+		if( !touch.handled ) {
+			var currentX = event.touches[0].clientX;
+			var currentY = event.touches[0].clientY;
+
+			// If the touch started off with two points and still has 
+			// two active touches; test for the pinch gesture
+			if( event.touches.length === 2 && touch.startCount === 2 ) {
+
+				// The current distance in pixels between the two touch points
+				var currentSpan = distanceBetween( {
+					x: event.touches[1].clientX,
+					y: event.touches[1].clientY
+				}, {
+					x: touch.startX,
+					y: touch.startY
+				} );
+
+				// If the span is larger than the desire amount we've got 
+				// ourselves a pinch
+				if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) {
+					touch.handled = true;
+
+					if( currentSpan < touch.startSpan ) {
+						activateOverview();
+					}
+					else {
+						deactivateOverview();
+					}
+				}
+
 			}
-			else if( point.y > window.innerHeight - ht ) {
-				navigateDown();
+			// There was only one touch point, look for a swipe
+			else if( event.touches.length === 1 ) {
+				var deltaX = currentX - touch.startX,
+					deltaY = currentY - touch.startY;
+
+				if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
+					touch.handled = true;
+					navigateLeft();
+				} 
+				else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
+					touch.handled = true;
+					navigateRight();
+				} 
+				else if( deltaY > touch.threshold ) {
+					touch.handled = true;
+					navigateUp();
+				} 
+				else if( deltaY < -touch.threshold ) {
+					touch.handled = true;
+					navigateDown();
+				}
 			}
-			
-			slide();
+
+			event.preventDefault();
 		}
 	}
 
+	/**
+	 * Handler for the document level 'touchend' event.
+	 */
+	function onDocumentTouchEnd( event ) {
+		touch.handled = false;
+	}
 
 	/**
 	 * Handles mouse wheel scrolling, throttled to avoid 
@@ -326,6 +408,7 @@ var Reveal = (function(){
 	 * can't be improved.
 	 */
 	function activateOverview() {
+		
 		dom.wrapper.classList.add( 'overview' );
 
 		var horizontalSlides = Array.prototype.slice.call( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
@@ -341,12 +424,12 @@ var Reveal = (function(){
 			hslide.style.msTransform = htransform;
 			hslide.style.OTransform = htransform;
 			hslide.style.transform = htransform;
-
+		
 			if( !hslide.classList.contains( 'stack' ) ) {
 				// Navigate to this slide on click
 				hslide.addEventListener( 'click', onOverviewSlideClicked, true );
 			}
-
+	
 			var verticalSlides = Array.prototype.slice.call( hslide.querySelectorAll( 'section' ) );
 
 			for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) {
@@ -365,6 +448,7 @@ var Reveal = (function(){
 				// Navigate to this slide on click
 				vslide.addEventListener( 'click', onOverviewSlideClicked, true );
 			}
+			
 		}
 	}
 	
@@ -786,6 +870,18 @@ var Reveal = (function(){
 			availableRoutes().down ? navigateDown() : navigateRight();
 		}
 	}
+
+	/**
+	 * Toggles the slide overview mode on and off.
+	 */
+	function toggleOverview() {
+		if( overviewIsActive() ) {
+			deactivateOverview();
+		}
+		else {
+			activateOverview();
+		}
+	}
 	
 	// Expose some methods publicly
 	return {
@@ -795,6 +891,7 @@ var Reveal = (function(){
 		navigateRight: navigateRight,
 		navigateUp: navigateUp,
 		navigateDown: navigateDown,
+		toggleOverview: toggleOverview,
 
 		// Forward event binding to the reveal DOM element
 		addEventListener: function( type, listener, useCapture ) {

File diff suppressed because it is too large
+ 0 - 0
js/reveal.min.js


Some files were not shown because too many files changed in this diff