From d324aea76132b3dc9cd7b199b2cc738b0f30254b Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Mon, 31 Mar 2014 12:54:37 -0700 Subject: [PATCH] Multi-Touch MouseTracker Update Refactored, fixed a couple bugs, unit tests working. Issuing a pull request. --- src/mousetracker.js | 381 +++++++++----------------------------- src/viewer.js | 1 + test/legacy.mouse.shim.js | 34 ++++ test/test.html | 3 +- 4 files changed, 125 insertions(+), 294 deletions(-) create mode 100644 test/legacy.mouse.shim.js diff --git a/src/mousetracker.js b/src/mousetracker.js index cc588a14..4134a1da 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -157,17 +157,6 @@ * Are we currently tracking mouse events. * @property {Boolean} capturing * Are we curruently capturing mouse events. - * @property {Boolean} insideElementPressed - * True if the left mouse button is currently being pressed and was - * initiated inside the tracked element, otherwise false. - * @property {Boolean} insideElement - * Are we currently inside the screen area of the tracked element. - * @property {OpenSeadragon.Point} lastPoint - * Position of last mouse down/move - * @property {Number} lastMouseDownTime - * Time of last mouse down. - * @property {OpenSeadragon.Point} lastMouseDownPoint - * Position of last mouse down */ THIS[ this.hash ] = { click: function ( event ) { onClick( _this, event ); }, @@ -215,6 +204,7 @@ tracking: false, capturing: false, + // Active Contact Points mousePoints: new $.MouseTracker.GesturePointList(), touchPoints: new $.MouseTracker.GesturePointList(), @@ -777,11 +767,11 @@ * @property {Number} id * Identifier unique from all other active GesturePoints for a given pointer device. * @property {String} type - * "mouse", "touch", "pen", or "". + * The pointer device type: "mouse", "touch", "pen", or "". * @property {Boolean} insideElementPressed * True if mouse button pressed or contact point initiated inside the screen area of the tracked element. * @property {Boolean} insideElement - * True if mouse cursor or contact point is currently inside the screen area of the tracked element. + * True if mouse cursor or contact point is currently inside the bounds of the tracked element. * @property {Number} speed * Continuously computed speed, in pixels per second. * @property {Number} direction @@ -1107,14 +1097,14 @@ * @inner */ function getMouseRelative( event, element ) { - return getPointRelative( getMouseAbsolute( event ), element ); + return getPointRelativeToAbsolute( getMouseAbsolute( event ), element ); } /** * @private * @inner */ - function getPointRelative( point, element ) { + function getPointRelativeToAbsolute( point, element ) { var offset = $.getElementOffset( element ); return point.minus( offset ); } @@ -1268,27 +1258,16 @@ * @inner */ function onMouseOver( tracker, event ) { - var time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //insideElementPressed: true, insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointersOver( tracker, event, [ gPoint ] ); @@ -1300,29 +1279,16 @@ * @inner */ function onMouseOut( tracker, event ) { - var time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); - var eventOrTouchPoint = event;//isTouch ? event.touches[ 0 ] : event; - - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //insideElementPressed: true, insideElement: false, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointersOut( tracker, event, [ gPoint ] ); @@ -1334,10 +1300,7 @@ * @inner */ function onMouseDown( tracker, event ) { - var delegate = THIS[ tracker.hash ], - time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); @@ -1345,21 +1308,11 @@ return; } - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - insideElementPressed: true, - insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; addPointers( tracker, event, [ gPoint ] ); @@ -1454,8 +1407,11 @@ * @inner */ function onMouseMove( tracker, event ) { - handleMouseMove( tracker, event ); - } + var delegate = THIS[ tracker.hash ]; + if ( !delegate.capturing ) { + handleMouseMove( tracker, event ); + } + } /** @@ -1491,29 +1447,17 @@ * @inner */ function onTouchEnter( tracker, event ) { - var time, - position, - i, + var i, touchCount = event.changedTouches.length, gPoints = []; - time = $.now(); - for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), + currentTime: $.now() } ); } @@ -1526,29 +1470,17 @@ * @inner */ function onTouchLeave( tracker, event ) { - var time, - position, - i, + var i, touchCount = event.changedTouches.length, gPoints = []; - time = $.now(); - for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, insideElement: false, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), + currentTime: $.now() } ); } @@ -1563,7 +1495,6 @@ function onTouchStart( tracker, event ) { var delegate = THIS[ tracker.hash ], time, - position, gPoint, i, touchCount = event.changedTouches.length, @@ -1575,13 +1506,7 @@ gPoint = { id: event.changedTouches[ 0 ].identifier, type: 'touch', - //insideElementPressed: true, insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), currentTime: time }; @@ -1589,19 +1514,10 @@ } for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - insideElementPressed: true, - insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), currentTime: time } ); } @@ -1623,7 +1539,6 @@ function onTouchEnd( tracker, event ) { var delegate = THIS[ tracker.hash ], time, - position, gPoint, i, touchCount = event.changedTouches.length, @@ -1632,19 +1547,10 @@ time = $.now(); for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), currentTime: time } ); } @@ -1655,13 +1561,7 @@ gPoint = { id: event.changedTouches[ 0 ].identifier, type: 'touch', - //insideElementPressed: true, insideElement: false, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), currentTime: time }; @@ -1681,29 +1581,16 @@ * @inner */ function onTouchMove( tracker, event ) { - var time, - position, - i, + var i, touchCount = event.changedTouches.length, gPoints = []; - time = $.now(); - for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), + currentTime: $.now() } ); } @@ -1722,29 +1609,14 @@ * @inner */ function onTouchCancel( tracker, event ) { - var //time, - //position, - i, + var i, touchCount = event.changedTouches.length, gPoints = []; - //time = $.now(); - for ( i = 0; i < touchCount; i++ ) { - //position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - //currentPos: position, - //currentTime: time } ); } @@ -1779,25 +1651,14 @@ * @inner */ function onPointerOver( tracker, event ) { - var time, - position, - gPoint; - - time = $.now(); - position = getMouseAbsolute( event ); + var gPoint; gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointersOver( tracker, event, [ gPoint ] ); @@ -1809,25 +1670,14 @@ * @inner */ function onPointerOut( tracker, event ) { - var time, - position, - gPoint; - - time = $.now(); - position = getMouseAbsolute( event ); + var gPoint; gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, insideElement: false, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointersOut( tracker, event, [ gPoint ] ); @@ -1839,10 +1689,7 @@ * @inner */ function onPointerDown( tracker, event ) { - var delegate = THIS[ tracker.hash ], - time, - position, - gPoint; + var gPoint; if ( event.button == 2 ) { return; @@ -1855,21 +1702,11 @@ event.currentTarget.msSetPointerCapture( event.pointerId ); } - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: event.pointerId, type: getPointerType( event ), - insideElementPressed: true, - insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; addPointers( tracker, event, [ gPoint ] ); @@ -1887,10 +1724,7 @@ * @inner */ function onPointerUp( tracker, event ) { - var delegate = THIS[ tracker.hash ], - time, - position, - gPoint; + var gPoint; if ( event.button == 2 ) { return; @@ -1903,21 +1737,11 @@ event.currentTarget.msReleasePointerCapture( event.pointerId ); } - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; removePointers(tracker, event, [ gPoint ]); @@ -1936,25 +1760,13 @@ */ function onPointerMove( tracker, event ) { // Pointer changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) - var time, - position, - gPoint; - - time = $.now(); - position = getMouseAbsolute( event ); + var gPoint; gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointers(tracker, event, [ gPoint ]); @@ -1972,25 +1784,11 @@ * @inner */ function onPointerCancel( tracker, event ) { - var //time, - //position, - gPoint; - - //time = $.now(); - //position = getMouseAbsolute( event ); + var gPoint; gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - //currentPos: position, - //currentTime: time }; cancelPointers( tracker, event, [ gPoint ] ); @@ -2045,27 +1843,15 @@ * @inner */ function handleMouseMove( tracker, event ) { - var time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointers( tracker, event, [ gPoint ] ); @@ -2077,9 +1863,7 @@ * @inner */ function handleMouseUp( tracker, event ) { - var time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); @@ -2087,21 +1871,11 @@ return; } - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; removePointers( tracker, event, [ gPoint ] ); @@ -2145,6 +1919,8 @@ curGPoint = gPoints[ i ]; // Initialize for drag/swipe/pinch + curGPoint.insideElementPressed = true; + curGPoint.insideElement = true; curGPoint.speed = 0; curGPoint.direction = 0; curGPoint.startPos = curGPoint.currentPos; @@ -2165,7 +1941,7 @@ { eventSource: tracker, pointerType: curGPoint.type, - position: getPointRelative( curGPoint.startPos, tracker.element ), + position: getPointRelativeToAbsolute( curGPoint.startPos, tracker.element ), isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, @@ -2227,7 +2003,7 @@ { eventSource: tracker, pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), insideElementPressed: insideElementPressed, buttonDownAny: IS_BUTTON_DOWN, isTouchEvent: curGPoint.type === 'touch', @@ -2282,7 +2058,7 @@ { eventSource: tracker, pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), insideElementPressed: insideElementPressed, buttonDownAny: IS_BUTTON_DOWN, isTouchEvent: curGPoint.type === 'touch', @@ -2340,7 +2116,26 @@ //} } - if ( pointsListLength == 1 ) { + if ( pointsListLength === 0 ) { + // Move (no contacts) + if ( tracker.moveHandler ) { + propagate = tracker.moveHandler( + { + eventSource: tracker, + pointerType: curGPoint.type, + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + } + else if ( pointsListLength === 1 ) { // Move (1 contact) if ( tracker.moveHandler ) { updateGPoint = pointsList.asArray()[ 0 ]; @@ -2348,7 +2143,7 @@ { eventSource: tracker, pointerType: updateGPoint.type, - position: getPointRelative( updateGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ), isTouchEvent: updateGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, @@ -2368,7 +2163,7 @@ { eventSource: tracker, pointerType: updateGPoint.type, - position: getPointRelative( updateGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ), delta: delta, speed: updateGPoint.speed, direction: updateGPoint.direction, @@ -2384,7 +2179,7 @@ } } } - else if ( pointsListLength == 2 ) { + else if ( pointsListLength === 2 ) { // Move (2 contacts, use center) if ( tracker.moveHandler ) { gPointArray = pointsList.asArray(); @@ -2392,7 +2187,7 @@ { eventSource: tracker, pointerType: gPointArray[ 0 ].type, - position: getPointRelative( getCenterPoint( gPointArray[ 0 ].currentPos, gPointArray[ 1 ].currentPos ), tracker.element ), + position: getPointRelativeToAbsolute( getCenterPoint( gPointArray[ 0 ].currentPos, gPointArray[ 1 ].currentPos ), tracker.element ), isTouchEvent: gPointArray[ 0 ].type === 'touch', originalEvent: event, preventDefaultAction: false, @@ -2417,8 +2212,8 @@ eventSource: tracker, pointerType: 'touch', gesturePoints: delegate.pinchGPoints, - lastCenter: getPointRelative( delegate.lastPinchCenter, tracker.element ), - center: getPointRelative( delegate.currentPinchCenter, tracker.element ), + lastCenter: getPointRelativeToAbsolute( delegate.lastPinchCenter, tracker.element ), + center: getPointRelativeToAbsolute( delegate.currentPinchCenter, tracker.element ), lastDistance: delegate.lastPinchDist, distance: delegate.currentPinchDist, shift: event.shiftKey, @@ -2476,7 +2271,7 @@ if ( pointsListLength === 0 ) { insideElementPressed = removedGPoint.insideElementPressed; - insideElementReleased = $.pointInElement( tracker.element, releasePoint ); + insideElementReleased = removedGPoint.insideElement || $.pointInElement( tracker.element, releasePoint ); // Release if ( tracker.releaseHandler ) { @@ -2484,7 +2279,7 @@ { eventSource: tracker, pointerType: removedGPoint.type, - position: getPointRelative( releasePoint, tracker.element ), + position: getPointRelativeToAbsolute( releasePoint, tracker.element ), insideElementPressed: insideElementPressed, insideElementReleased: insideElementReleased, isTouchEvent: removedGPoint.type === 'touch', @@ -2504,7 +2299,7 @@ { eventSource: tracker, pointerType: removedGPoint.type, - position: getPointRelative( removedGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( removedGPoint.currentPos, tracker.element ), speed: removedGPoint.speed, direction: removedGPoint.direction, shift: event.shiftKey, @@ -2530,7 +2325,7 @@ { eventSource: tracker, pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), quick: quick, shift: event.shiftKey, isTouchEvent: curGPoint.type === 'touch', diff --git a/src/viewer.js b/src/viewer.js index 7092046e..79ad821f 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2281,6 +2281,7 @@ function onCanvasDrag( event ) { } function onCanvasDragEnd( event ) { + // TODO: Make the magic numbers configurable (20 pixels-per-second speed threshold and 0.35 momentum dampener). if ( event.pointerType !== 'mouse' && !event.preventDefaultAction && this.viewport && event.speed > 20 ) { var amplitudeX = 0.35 * ( event.speed * Math.cos( event.direction ) ), amplitudeY = 0.35 * ( event.speed * Math.sin( event.direction ) ), diff --git a/test/legacy.mouse.shim.js b/test/legacy.mouse.shim.js new file mode 100644 index 00000000..8a503759 --- /dev/null +++ b/test/legacy.mouse.shim.js @@ -0,0 +1,34 @@ +(function($, undefined) { + + /** + * Plugin to force OpenSeadragon to use the legacy mouse pointer event model + */ + + $.MouseTracker.subscribeEvents = [ "click", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; + + if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { + // Older Firefox + $.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" ); + } + + $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout", "mousedown", "mouseup", "mousemove" ); + if ( 'ontouchstart' in window ) { + // iOS, Android, and other W3c Touch Event implementations (see http://www.w3.org/TR/2011/WD-touch-events-20110505) + $.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" ); + if ( 'ontouchenter' in window ) { + $.MouseTracker.subscribeEvents.push( "touchenter", "touchleave" ); + $.MouseTracker.haveTouchEnter = true; + } + else { + $.MouseTracker.haveTouchEnter = false; + } + } + if ( 'ongesturestart' in window ) { + // iOS (see https://developer.apple.com/library/safari/documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html) + // Subscribe to these to prevent default gesture handling + $.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" ); + } + $.MouseTracker.mousePointerId = "legacy-mouse"; + $.MouseTracker.maxTouchPoints = 10; + +}(OpenSeadragon)); diff --git a/test/test.html b/test/test.html index 94602d88..c6159545 100644 --- a/test/test.html +++ b/test/test.html @@ -1,4 +1,4 @@ - + @@ -15,6 +15,7 @@ +