Fallback to mouseover/mouseout,

This commit is contained in:
Mark Salsbery 2014-04-15 13:04:08 -07:00
parent e8e0f28f5a
commit 9b15ca090b
4 changed files with 201 additions and 72 deletions

View File

@ -8,7 +8,6 @@ OPENSEADRAGON CHANGELOG
* The overlays elements are no longer accessible via viewer.canvas.childNodes but via viewer.overlaysContainer.childNodes or viewer.currentOverlays[i].element. * The overlays elements are no longer accessible via viewer.canvas.childNodes but via viewer.overlaysContainer.childNodes or viewer.currentOverlays[i].element.
* BREAKING CHANGE: Pseudo full screen mode on IE<11 using activex has been dropped. OpenSeadragon will run in full page if full screen mode is requested. * BREAKING CHANGE: Pseudo full screen mode on IE<11 using activex has been dropped. OpenSeadragon will run in full page if full screen mode is requested.
* BREAKING CHANGE: MouseTracker touch pinch gestures are no longer converted to scroll events. MouseTracker.pinchHandler should be used instead. (#369) * BREAKING CHANGE: MouseTracker touch pinch gestures are no longer converted to scroll events. MouseTracker.pinchHandler should be used instead. (#369)
* BREAKING CHANGE: MouseTracker now uses pointer enter/leave DOM events instead of pointer over/out DOM events for generating enterHandler/exitHandler events and for tracking pointers inside the hit-test bounds of the tracked element. (#369)
* DEPRECATION: overlay functions have been moved from Drawer to Viewer (#331) * DEPRECATION: overlay functions have been moved from Drawer to Viewer (#331)
* DEPRECATION: OpenSeadragon.cancelFullScreen has been renamed OpenSeadragon.exitFullScreen (#358) * DEPRECATION: OpenSeadragon.cancelFullScreen has been renamed OpenSeadragon.exitFullScreen (#358)
* DEPRECATION: The 'isTouchEvent' property passed in MouseTracker events is deprecated and has been replaced with 'pointerType', which is a String value "mouse", "touch", "pen", etc. to support multiple simultaneous pointing devices (#369) * DEPRECATION: The 'isTouchEvent' property passed in MouseTracker events is deprecated and has been replaced with 'pointerType', which is a String value "mouse", "touch", "pen", etc. to support multiple simultaneous pointing devices (#369)

View File

@ -163,6 +163,8 @@
DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); }, DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); },
MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); }, MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); },
mouseover: function ( event ) { onMouseOver( _this, event ); },
mouseout: function ( event ) { onMouseOut( _this, event ); },
mouseenter: function ( event ) { onMouseEnter( _this, event ); }, mouseenter: function ( event ) { onMouseEnter( _this, event ); },
mouseleave: function ( event ) { onMouseLeave( _this, event ); }, mouseleave: function ( event ) { onMouseLeave( _this, event ); },
mousedown: function ( event ) { onMouseDown( _this, event ); }, mousedown: function ( event ) { onMouseDown( _this, event ); },
@ -761,6 +763,7 @@
$.MouseTracker.maxTouchPoints = 0; $.MouseTracker.maxTouchPoints = 0;
} }
$.MouseTracker.haveTouchEnter = true; $.MouseTracker.haveTouchEnter = true;
$.MouseTracker.haveMouseEnter = true;
} else if ( window.MSPointerEvent ) { } else if ( window.MSPointerEvent ) {
// IE10 // IE10
$.MouseTracker.subscribeEvents.push( "MSPointerEnter", "MSPointerLeave", "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerCancel" ); $.MouseTracker.subscribeEvents.push( "MSPointerEnter", "MSPointerLeave", "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerCancel" );
@ -771,9 +774,17 @@
$.MouseTracker.maxTouchPoints = 0; $.MouseTracker.maxTouchPoints = 0;
} }
$.MouseTracker.haveTouchEnter = true; $.MouseTracker.haveTouchEnter = true;
$.MouseTracker.haveMouseEnter = true;
} else { } else {
// Legacy W3C mouse events // Legacy W3C mouse events
$.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave", "mousedown", "mouseup", "mousemove" ); $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
if ( 'onmouseenter' in window ) {
$.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" );
$.MouseTracker.haveMouseEnter = true;
} else {
$.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" );
$.MouseTracker.haveMouseEnter = false;
}
if ( 'ontouchstart' in window ) { if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations (see http://www.w3.org/TR/2011/WD-touch-events-20110505) // 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" ); $.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" );
@ -862,7 +873,6 @@
this.buttons = 0; this.buttons = 0;
/** /**
* Current number of contact points (touch points, mouse down, etc.) for the device. * Current number of contact points (touch points, mouse down, etc.) for the device.
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
* @member {Number} contacts * @member {Number} contacts
* @memberof OpenSeadragon.MouseTracker.GesturePointList# * @memberof OpenSeadragon.MouseTracker.GesturePointList#
*/ */
@ -1313,6 +1323,72 @@
} }
/**
* @private
* @inner
*/
function isParentChild( parent, child )
{
if ( parent === child ) {
return false;
}
while ( child && child !== parent ) {
child = child.parentNode;
}
return child === parent;
}
/**
* @private
* @inner
*/
function onMouseOver( tracker, event ) {
var gPoint;
event = $.getEvent( event );
if ( this === event.relatedTarget || isParentChild( this, event.relatedTarget ) ) {
return;
}
gPoint = {
id: $.MouseTracker.mousePointerId,
type: 'mouse',
isPrimary: true,
currentPos: getMouseAbsolute( event ),
currentTime: $.now()
};
updatePointersEnter( tracker, event, [ gPoint ] );
}
/**
* @private
* @inner
*/
function onMouseOut( tracker, event ) {
var gPoint;
event = $.getEvent( event );
if ( this === event.relatedTarget || isParentChild( this, event.relatedTarget ) ) {
return;
}
gPoint = {
id: $.MouseTracker.mousePointerId,
type: 'mouse',
isPrimary: true,
currentPos: getMouseAbsolute( event ),
currentTime: $.now()
};
updatePointersExit( tracker, event, [ gPoint ] );
}
/** /**
* @private * @private
* @inner * @inner
@ -1330,7 +1406,7 @@
currentTime: $.now() currentTime: $.now()
}; };
updatePointersOver( tracker, event, [ gPoint ] ); updatePointersEnter( tracker, event, [ gPoint ] );
} }
@ -1351,7 +1427,7 @@
currentTime: $.now() currentTime: $.now()
}; };
updatePointersOut( tracker, event, [ gPoint ] ); updatePointersExit( tracker, event, [ gPoint ] );
} }
@ -1493,7 +1569,7 @@
} ); } );
} }
updatePointersOver( tracker, event, gPoints ); updatePointersEnter( tracker, event, gPoints );
} }
@ -1516,7 +1592,7 @@
} ); } );
} }
updatePointersOut( tracker, event, gPoints ); updatePointersExit( tracker, event, gPoints );
} }
@ -1544,7 +1620,7 @@
// simulate touchenter if not natively available // simulate touchenter if not natively available
if ( !$.MouseTracker.haveTouchEnter ) { if ( !$.MouseTracker.haveTouchEnter ) {
updatePointersOver( tracker, event, gPoints ); updatePointersEnter( tracker, event, gPoints );
} }
if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact
@ -1584,7 +1660,7 @@
// simulate touchleave if not natively available // simulate touchleave if not natively available
if ( !$.MouseTracker.haveTouchEnter && touchCount > 0 ) { if ( !$.MouseTracker.haveTouchEnter && touchCount > 0 ) {
updatePointersOut( tracker, event, gPoints ); updatePointersExit( tracker, event, gPoints );
} }
$.stopEvent( event ); $.stopEvent( event );
@ -1675,7 +1751,7 @@
currentTime: $.now() currentTime: $.now()
}; };
updatePointersOver( tracker, event, [ gPoint ] ); updatePointersEnter( tracker, event, [ gPoint ] );
} }
@ -1694,7 +1770,7 @@
currentTime: $.now() currentTime: $.now()
}; };
updatePointersOut( tracker, event, [ gPoint ] ); updatePointersExit( tracker, event, [ gPoint ] );
} }
@ -1871,7 +1947,7 @@
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
* Gesture points associated with the event. * Gesture points associated with the event.
*/ */
function updatePointersOver( tracker, event, gPoints ) { function updatePointersEnter( tracker, event, gPoints ) {
var pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ), var pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
i, i,
gPointCount = gPoints.length, gPointCount = gPoints.length,
@ -1935,7 +2011,7 @@
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
* Gesture points associated with the event. * Gesture points associated with the event.
*/ */
function updatePointersOut( tracker, event, gPoints ) { function updatePointersExit( tracker, event, gPoints ) {
var delegate = THIS[ tracker.hash ], var delegate = THIS[ tracker.hash ],
pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ), pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
i, i,

View File

@ -27,8 +27,10 @@
// ---------- // ----------
asyncTest( 'MouseTracker: mouse gestures', function () { asyncTest( 'MouseTracker: mouse gestures', function () {
var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ), var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ),
simEvent = {},
offset = $canvas.offset(), offset = $canvas.offset(),
tracker = viewer.innerTracker, tracker = viewer.innerTracker,
intervalId,
origEnterHandler, origEnterHandler,
origExitHandler, origExitHandler,
origPressHandler, origPressHandler,
@ -47,7 +49,9 @@
dragEndCount, dragEndCount,
insideElementPressed, insideElementPressed,
insideElementReleased, insideElementReleased,
quickClick; quickClick,
speed,
direction;
var hookViewerHandlers = function () { var hookViewerHandlers = function () {
origEnterHandler = tracker.enterHandler; origEnterHandler = tracker.enterHandler;
@ -119,6 +123,8 @@
origDragEndHandler = tracker.dragEndHandler; origDragEndHandler = tracker.dragEndHandler;
tracker.dragEndHandler = function ( event ) { tracker.dragEndHandler = function ( event ) {
dragEndCount++; dragEndCount++;
speed = event.speed;
direction = event.direction;
if (origDragEndHandler) { if (origDragEndHandler) {
return origDragEndHandler( event ); return origDragEndHandler( event );
} else { } else {
@ -138,40 +144,76 @@
tracker.dragEndHandler = origDragEndHandler; tracker.dragEndHandler = origDragEndHandler;
}; };
var simulateEnter = function ($element, event, x, y) { var simulateEnter = function ($element, x, y) {
event.clientX = offset.left + x; simEvent.clientX = offset.left + x;
event.clientY = offset.top + y; simEvent.clientY = offset.top + y;
$canvas.simulate( 'mouseenter', event ); $canvas.simulate( 'mouseenter', simEvent );
}; };
var simulateLeave = function ($element, event, x, y) { var simulateLeave = function ($element, x, y) {
event.clientX = offset.left + x; simEvent.clientX = offset.left + x;
event.clientY = offset.top + y; simEvent.clientY = offset.top + y;
$canvas.simulate( 'mouseleave', event ); $canvas.simulate( 'mouseleave', simEvent );
}; };
var simulateMove = function ($element, event, dX, dY, count) { var simulateMove = function ($element, dX, dY, count) {
var i; var i;
for ( i = 0; i < count; i++ ) { for ( i = 0; i < count; i++ ) {
event.clientX += dX; simEvent.clientX += dX;
event.clientY += dY; simEvent.clientY += dY;
$canvas.simulate( 'mousemove', event ); $canvas.simulate( 'mousemove', simEvent );
} }
}; };
var simulateDown = function ($element, event, x, y) { var simulateTimedMove = function ($element, dX, dY, count, ms, doneCallback) {
event.clientX = offset.left + x; var msInterval = Math.floor(ms / count),
event.clientY = offset.top + y; dX = dX,
$canvas.simulate( 'mousedown', event ); dY = dY,
i = count,
doneCallback = doneCallback,
moves = 0;
intervalId = window.setInterval(function () {
if (i > 0) {
moves++;
//simEvent.clientX += dX;
//simEvent.clientY += dY;
//$canvas.simulate( 'mousemove', simEvent );
}
i--;
if (i === 0) {
window.clearInterval( intervalId );
doneCallback();
}
}, msInterval );
//for ( i = 0; i < count; i++ ) {
// simEvent.clientX += dX;
// simEvent.clientY += dY;
// $canvas.simulate( 'mousemove', simEvent );
// targetTime = msWait + OpenSeadragon.now();
// while (OpenSeadragon.now() < targetTime) {}
//}
}; };
var simulateUp = function ($element, event, x, y) { var simulateDown = function ($element, x, y) {
event.clientX = offset.left + x; simEvent.clientX = offset.left + x;
event.clientY = offset.top + y; simEvent.clientY = offset.top + y;
$canvas.simulate( 'mouseup', event ); $canvas.simulate( 'mousedown', simEvent );
};
var simulateUp = function ($element, x, y) {
simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y;
$canvas.simulate( 'mouseup', simEvent );
}; };
var resetForAssessment = function () { var resetForAssessment = function () {
simEvent = {
clientX: offset.left,
clientY: offset.top
};
enterCount = 0; enterCount = 0;
exitCount = 0; exitCount = 0;
pressCount = 0; pressCount = 0;
@ -183,6 +225,8 @@
insideElementPressed = false; insideElementPressed = false;
insideElementReleased = false; insideElementReleased = false;
quickClick = false; quickClick = false;
speed = 0;
direction = 2 * Math.PI;
}; };
var assessGestureExpectations = function (expected) { var assessGestureExpectations = function (expected) {
@ -226,9 +270,18 @@
if ('quickClick' in expected) { if ('quickClick' in expected) {
equal( quickClick, expected.quickClick, expected.description + 'clickHandler event.quick matches expected (' + expected.quickClick + ')' ); equal( quickClick, expected.quickClick, expected.description + 'clickHandler event.quick matches expected (' + expected.quickClick + ')' );
} }
if ('speed' in expected) {
equal( speed, expected.speed, expected.description + 'Drag speed matches expected (' + expected.speed + ')' );
}
if ('direction' in expected) {
equal( direction, expected.direction, expected.description + 'Drag direction matches expected (' + expected.direction + ')' );
}
}; };
var onOpen = function ( event ) { var onOpen = function ( event ) {
var timeStart,
timeElapsed;
viewer.removeHandler( 'open', onOpen ); viewer.removeHandler( 'open', onOpen );
hookViewerHandlers(); hookViewerHandlers();
@ -236,11 +289,10 @@
// enter-move-release (release in tracked element, press in unknown element) // enter-move-release (release in tracked element, press in unknown element)
// (Note we also test to see if the pointer is still being tracked by not simulating a leave event until after assessment) // (Note we also test to see if the pointer is still being tracked by not simulating a leave event until after assessment)
resetForAssessment(); resetForAssessment();
var event = {}; simulateEnter($canvas, 0, 0);
simulateEnter($canvas, event, 0, 0); simulateMove($canvas, 1, 1, 10);
simulateMove($canvas, event, 1, 1, 10); simulateMove($canvas, -1, -1, 10);
simulateMove($canvas, event, -1, -1, 10); simulateUp($canvas, 0, 0);
simulateUp($canvas, event, 0, 0);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-move-release (release in tracked element, press in unknown element): ', description: 'enter-move-release (release in tracked element, press in unknown element): ',
enterCount: 1, enterCount: 1,
@ -257,15 +309,14 @@
trackedPointers: 1 trackedPointers: 1
//quickClick: false //quickClick: false
}); });
simulateLeave($canvas, event, -1, -1); // flush tracked pointer simulateLeave($canvas, -1, -1); // flush tracked pointer
// enter-move-exit (fly-over) // enter-move-exit (fly-over)
resetForAssessment(); resetForAssessment();
var event = {}; simulateEnter($canvas, 0, 0);
simulateEnter($canvas, event, 0, 0); simulateMove($canvas, 1, 1, 10);
simulateMove($canvas, event, 1, 1, 10); simulateMove($canvas, -1, -1, 10);
simulateMove($canvas, event, -1, -1, 10); simulateLeave($canvas, -1, -1);
simulateLeave($canvas, event, -1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-move-exit (fly-over): ', description: 'enter-move-exit (fly-over): ',
enterCount: 1, enterCount: 1,
@ -285,10 +336,9 @@
// move-exit (fly-over, no enter event) // move-exit (fly-over, no enter event)
resetForAssessment(); resetForAssessment();
var event = {}; simulateMove($canvas, 1, 1, 10);
simulateMove($canvas, event, 1, 1, 10); simulateMove($canvas, -1, -1, 10);
simulateMove($canvas, event, -1, -1, 10); simulateLeave($canvas, -1, -1);
simulateLeave($canvas, event, -1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'move-exit (fly-over, no enter event): ', description: 'move-exit (fly-over, no enter event): ',
enterCount: 0, enterCount: 0,
@ -308,11 +358,10 @@
// enter-press-release-exit // enter-press-release-exit
resetForAssessment(); resetForAssessment();
var event = {}; simulateEnter($canvas, 0, 0);
simulateEnter($canvas, event, 0, 0); simulateDown($canvas, 0, 0);
simulateDown($canvas, event, 0, 0); simulateUp($canvas, 0, 0);
simulateUp($canvas, event, 0, 0); simulateLeave($canvas, -1, -1);
simulateLeave($canvas, event, -1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-press-release-exit (click): ', description: 'enter-press-release-exit (click): ',
enterCount: 1, enterCount: 1,
@ -332,22 +381,21 @@
// enter-press-move-release-move-exit (drag, release in tracked element) // enter-press-move-release-move-exit (drag, release in tracked element)
resetForAssessment(); resetForAssessment();
var event = {}; simulateEnter($canvas, 0, 0);
simulateEnter($canvas, event, 0, 0); simulateDown($canvas, 0, 0);
simulateDown($canvas, event, 0, 0); simulateMove($canvas, 1, 1, 100);
simulateMove($canvas, event, 1, 1, 10); simulateUp($canvas, 10, 10);
simulateUp($canvas, event, 10, 10); simulateMove($canvas, -1, -1, 100);
simulateMove($canvas, event, -1, -1, 10); simulateLeave($canvas, -1, -1);
simulateLeave($canvas, event, -1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-press-move-release-move-exit (drag, release in tracked element): ', description: 'enter-press-move-release-move-exit (drag, release in tracked element): ',
enterCount: 1, enterCount: 1,
exitCount: 1, exitCount: 1,
pressCount: 1, pressCount: 1,
releaseCount: 1, releaseCount: 1,
moveCount: 20, moveCount: 200,
clickCount: 1, clickCount: 1,
dragCount: 10, dragCount: 100,
dragEndCount: 1, dragEndCount: 1,
insideElementPressed: true, insideElementPressed: true,
insideElementReleased: true, insideElementReleased: true,
@ -358,14 +406,13 @@
// enter-press-move-exit-move-release (drag, release outside tracked element) // enter-press-move-exit-move-release (drag, release outside tracked element)
resetForAssessment(); resetForAssessment();
var event = {}; simulateEnter($canvas, 0, 0);
simulateEnter($canvas, event, 0, 0); simulateDown($canvas, 0, 0);
simulateDown($canvas, event, 0, 0); simulateMove($canvas, 1, 1, 5);
simulateMove($canvas, event, 1, 1, 5); simulateMove($canvas, -1, -1, 5);
simulateMove($canvas, event, -1, -1, 5); simulateLeave($canvas, -1, -1);
simulateLeave($canvas, event, -1, -1); simulateMove($canvas, -1, -1, 5);
simulateMove($canvas, event, -1, -1, 5); simulateUp($canvas, -5, -5);
simulateUp($canvas, event, -5, -5);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-press-move-exit-move-release (drag, release outside tracked element): ', description: 'enter-press-move-exit-move-release (drag, release outside tracked element): ',
enterCount: 1, enterCount: 1,

View File

@ -11,7 +11,14 @@
$.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" ); $.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" );
} }
$.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave", "mousedown", "mouseup", "mousemove" ); $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
if ( 'onmouseenter' in window ) {
$.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" );
$.MouseTracker.haveMouseEnter = true;
} else {
$.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" );
$.MouseTracker.haveMouseEnter = false;
}
if ( 'ontouchstart' in window ) { if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations (see http://www.w3.org/TR/2011/WD-touch-events-20110505) // 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" ); $.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" );