From 50d62ca9697600d9a45cacd24b962f97a004d7b9 Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 3 Dec 2015 13:37:11 +0000 Subject: [PATCH] Added touch support and partial pinch zoom support (although it misbehaves in safari so its off by default but can be turned on by the opts). Tested in Safari iPad2 and Chrome in Galaxy S4 --- globe/globe.js | 238 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 3 deletions(-) diff --git a/globe/globe.js b/globe/globe.js index 806e5697..b37b1036 100644 --- a/globe/globe.js +++ b/globe/globe.js @@ -85,6 +85,227 @@ DAT.Globe = function(container, opts) { var padding = 40; var PI_HALF = Math.PI / 2; + + var touchHandled; + var isPinchScaling; + var pinchStartX; + var clickTimer; + + //disabled pinch zooming by default due to as weird bugs in Safari but leaving in, in case someone else can fix it + var pinchZoomEnabled = opts.pinchZoomEnabled || false; + + /** + * (Modified from `jquery-ui-touchpunch`) + * Simulate a mouse event based on a corresponding touch event + * @param {Object} event A touch event + * @param {String} simulatedType The corresponding mouse event + */ + function simulateMouseEvent(event, simulatedType) { + + var touch; + + // Ignore multi-touch events + if(event.originalEvent){ + touch = event.originalEvent.changedTouches[0]; + + if (event.originalEvent.touches.length == 2) { + //handle pinching + isPinchScaling = true; + pinchStartX = event.originalEvent.touches[0].clientX; + onPinch(event); + + return; + } + else if (event.originalEvent.touches.length > 2){ + return; + } + } + else if(event.touches){ + touch = event.changedTouches[0]; + + if(event.touches.length == 2){ + //handle pinching + isPinchScaling = true; + pinchStartX = event.touches[0].clientX; + onPinch(event); + + return; + } + else if(event.touches.length > 2){ + return; + } + } + + event.preventDefault(); + + var simulatedEvent = document.createEvent('MouseEvents'); + + // Initialize the simulated mouse event using the touch event's coordinates + simulatedEvent.initMouseEvent( + simulatedType, // type + true, // bubbles + true, // cancelable + window, // view + 1, // detail + touch.screenX, // screenX + touch.screenY, // screenY + touch.clientX, // clientX + touch.clientY, // clientY + false, // ctrlKey + false, // altKey + false, // shiftKey + false, // metaKey + 0, // button + null // relatedTarget + ); + + // Dispatch the simulated event to the target element + event.target.dispatchEvent(simulatedEvent); + } + + function resetZoomOnDoubleTap(event){ + // check for double tap + if(pinchZoomEnabled) { + if ((event.originalEvent && event.originalEvent.touches.length == 1) || + (event.touches && event.touches.length == 1)) { + + if (clickTimer == null) { + clickTimer = setTimeout(function () { + clickTimer = null; + }, 500) + } else { + clearTimeout(clickTimer); + clickTimer = null; + + //reset zoom + zoom(-5000); + } + } + } + } + + function _onTouchStart (event) { + + resetZoomOnDoubleTap(event); + + var self = this; + + // Ignore the event if another widget is already being handled + if (touchHandled){ + return; + } + + // Set the flag to prevent other widgets from inheriting the touch event + touchHandled = true; + + // Track movement to determine if interaction was a click + self._touchMoved = false; + + // Simulate the mouseover event + simulateMouseEvent(event, 'mouseover'); + + // Simulate the mousemove event + simulateMouseEvent(event, 'mousemove'); + + // Simulate the mousedown event + simulateMouseEvent(event, 'mousedown'); + } + + + function _onTouchMove(event) { + if(isPinchScaling){ + onPinch(event); + return; + } + + // Ignore event if not handled + if (!touchHandled) { + return; + } + + // Interaction was not a click + this._touchMoved = true; + + // Simulate the mousemove event + simulateMouseEvent(event, 'mousemove'); + } + + function _onTouchEnd(event) { + + if(isPinchScaling){ + onPinch(event); + isPinchScaling = false + return; + } + + // Ignore event if not handled + if (!touchHandled) { + return; + } + + // Simulate the mouseup event + simulateMouseEvent(event, 'mouseup'); + + // Simulate the mouseout event + simulateMouseEvent(event, 'mouseout'); + + // If the touch interaction did not move, it should trigger a click + if (!this._touchMoved) { + + // Simulate the click event + simulateMouseEvent(event, 'click'); + } + + // Unset the flag to allow other widgets to inherit the touch event + touchHandled = false; + } + + function getPinchDistance(event){ + var dist; + + try{ + dist = Math.sqrt( + (event.touches[0].clientX-event.touches[1].clientX) * (event.touches[0].clientX-event.touches[1].clientX) + + (event.touches[0].clientY-event.touches[1].clientY) * (event.touches[0].clientY-event.touches[1].clientY) + ); + + dist = dist * 0.1; + dist = Math.min(dist, 25); + + var dir = (pinchStartX > event.touches[0].clientX) ? 1 : -1; + + dist = dist * dir; + } + catch(e){ + dist = null; + } + + return dist; + } + + function onPinch(event){ + if(pinchZoomEnabled){ + var dist = getPinchDistance(event) + if(dist != null){ + zoom(dist); + } + } + } + + + function addTouchHandlers(container){ + + var touchSupport = 'ontouchend' in document; + + //ignore browsers without touch + if(!touchSupport){ + return; + } + + container.addEventListener('touchstart', _onTouchStart, false); + } + + function init() { container.style.color = '#fff'; @@ -152,6 +373,8 @@ DAT.Globe = function(container, opts) { container.addEventListener('mousewheel', onMouseWheel, false); + addTouchHandlers(container); + document.addEventListener('keydown', onDocumentKeyDown, false); window.addEventListener('resize', onWindowResize, false); @@ -277,6 +500,11 @@ DAT.Globe = function(container, opts) { container.addEventListener('mouseup', onMouseUp, false); container.addEventListener('mouseout', onMouseOut, false); + container.addEventListener('touchend', _onTouchEnd, false); + container.addEventListener('touchmove', _onTouchMove, false); + + + mouseOnDown.x = - event.clientX; mouseOnDown.y = event.clientY; @@ -333,10 +561,14 @@ DAT.Globe = function(container, opts) { } } + function onWindowResize( event ) { - camera.aspect = container.offsetWidth / container.offsetHeight; - camera.updateProjectionMatrix(); - renderer.setSize( container.offsetWidth, container.offsetHeight ); + // this method fights with pinch zooming, needs to be disabled if pinch zooming + if(pinchZoomEnabled){ + camera.aspect = container.offsetWidth / container.offsetHeight; + camera.updateProjectionMatrix(); + renderer.setSize( container.offsetWidth, container.offsetHeight ); + } } function zoom(delta) {