mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-22 05:06:09 +03:00
Merge pull request #133 from iangilman/raf
Improved requestAnimationFrame polyfill; fixed timer leaks; added tests
This commit is contained in:
commit
6e9a300d1a
@ -4,6 +4,7 @@ OPENSEADRAGON CHANGELOG
|
|||||||
0.9.129: (In Progress)
|
0.9.129: (In Progress)
|
||||||
|
|
||||||
* Fixed an error when using navPrevNextWrap on single images (#135)
|
* Fixed an error when using navPrevNextWrap on single images (#135)
|
||||||
|
* Various fixes to our timer handling (#133)
|
||||||
|
|
||||||
0.9.128:
|
0.9.128:
|
||||||
|
|
||||||
|
@ -1689,6 +1689,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
var aAnimQueue = [],
|
var aAnimQueue = [],
|
||||||
|
processing = [],
|
||||||
iRequestId = 0,
|
iRequestId = 0,
|
||||||
iIntervalId;
|
iIntervalId;
|
||||||
|
|
||||||
@ -1699,7 +1700,18 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
|||||||
if ( !iIntervalId ) {
|
if ( !iIntervalId ) {
|
||||||
iIntervalId = setInterval( function() {
|
iIntervalId = setInterval( function() {
|
||||||
if ( aAnimQueue.length ) {
|
if ( aAnimQueue.length ) {
|
||||||
aAnimQueue.shift( )[ 1 ](+new Date());
|
var time = new Date().getTime();
|
||||||
|
// Process all of the currently outstanding frame
|
||||||
|
// requests, but none that get added during the
|
||||||
|
// processing.
|
||||||
|
// Swap the arrays so we don't have to create a new
|
||||||
|
// array every frame.
|
||||||
|
var temp = processing;
|
||||||
|
processing = aAnimQueue;
|
||||||
|
aAnimQueue = temp;
|
||||||
|
while ( processing.length ) {
|
||||||
|
processing.shift()[ 1 ]( time );
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// don't continue the interval, if unnecessary
|
// don't continue the interval, if unnecessary
|
||||||
clearInterval( iIntervalId );
|
clearInterval( iIntervalId );
|
||||||
@ -1714,12 +1726,23 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
|||||||
// create a mock cancelAnimationFrame function
|
// create a mock cancelAnimationFrame function
|
||||||
$.cancelAnimationFrame = function( requestId ) {
|
$.cancelAnimationFrame = function( requestId ) {
|
||||||
// find the request ID and remove it
|
// find the request ID and remove it
|
||||||
for ( var i = 0, j = aAnimQueue.length; i < j; i += 1 ) {
|
var i, j;
|
||||||
|
for ( i = 0, j = aAnimQueue.length; i < j; i += 1 ) {
|
||||||
if ( aAnimQueue[ i ][ 0 ] === requestId ) {
|
if ( aAnimQueue[ i ][ 0 ] === requestId ) {
|
||||||
aAnimQueue.splice( i, 1 );
|
aAnimQueue.splice( i, 1 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's not in the queue, it may be in the set we're currently
|
||||||
|
// processing (if cancelAnimationFrame is called from within a
|
||||||
|
// requestAnimationFrame callback).
|
||||||
|
for ( i = 0, j = processing.length; i < j; i += 1 ) {
|
||||||
|
if ( processing[ i ][ 0 ] === requestId ) {
|
||||||
|
processing.splice( i, 1 );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})( window );
|
})( window );
|
||||||
|
@ -151,7 +151,6 @@ $.Viewer = function( options ) {
|
|||||||
THIS[ this.hash ] = {
|
THIS[ this.hash ] = {
|
||||||
"fsBoundsDelta": new $.Point( 1, 1 ),
|
"fsBoundsDelta": new $.Point( 1, 1 ),
|
||||||
"prevContainerSize": null,
|
"prevContainerSize": null,
|
||||||
"updateRequestId": null,
|
|
||||||
"animating": false,
|
"animating": false,
|
||||||
"forceRedraw": false,
|
"forceRedraw": false,
|
||||||
"mouseInside": false,
|
"mouseInside": false,
|
||||||
@ -168,6 +167,8 @@ $.Viewer = function( options ) {
|
|||||||
"onfullscreenchange": null
|
"onfullscreenchange": null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this._updateRequestId = null;
|
||||||
|
|
||||||
//Inherit some behaviors and properties
|
//Inherit some behaviors and properties
|
||||||
$.EventHandler.call( this );
|
$.EventHandler.call( this );
|
||||||
$.ControlDock.call( this, options );
|
$.ControlDock.call( this, options );
|
||||||
@ -461,12 +462,16 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
|
|||||||
* @return {OpenSeadragon.Viewer} Chainable.
|
* @return {OpenSeadragon.Viewer} Chainable.
|
||||||
*/
|
*/
|
||||||
close: function ( ) {
|
close: function ( ) {
|
||||||
if ( THIS[ this.hash ].updateRequestId !== null ){
|
if ( this._updateRequestId !== null ) {
|
||||||
$.cancelAnimationFrame( THIS[ this.hash ].updateRequestId );
|
$.cancelAnimationFrame( this._updateRequestId );
|
||||||
THIS[ this.hash ].updateRequestId = null;
|
this._updateRequestId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( this.drawer ){
|
if ( this.navigator ) {
|
||||||
|
this.navigator.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.drawer ) {
|
||||||
this.drawer.clearOverlays();
|
this.drawer.clearOverlays();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1147,16 +1152,18 @@ function openTileSource( viewer, source ) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Instantiate a navigator if configured
|
//Instantiate a navigator if configured
|
||||||
if ( _this.showNavigator && ! _this.navigator && !_this.collectionMode ){
|
if ( _this.showNavigator && !_this.collectionMode ){
|
||||||
|
// Note: By passing the fully parsed source, the navigator doesn't
|
||||||
|
// have to load it again.
|
||||||
|
if ( _this.navigator ) {
|
||||||
|
_this.navigator.open( source );
|
||||||
|
} else {
|
||||||
_this.navigator = new $.Navigator({
|
_this.navigator = new $.Navigator({
|
||||||
id: _this.navigatorId,
|
id: _this.navigatorId,
|
||||||
position: _this.navigatorPosition,
|
position: _this.navigatorPosition,
|
||||||
sizeRatio: _this.navigatorSizeRatio,
|
sizeRatio: _this.navigatorSizeRatio,
|
||||||
height: _this.navigatorHeight,
|
height: _this.navigatorHeight,
|
||||||
width: _this.navigatorWidth,
|
width: _this.navigatorWidth,
|
||||||
// By passing the fully parsed source here, the navigator doesn't
|
|
||||||
// have to load it again. Additionally, we don't have to call
|
|
||||||
// navigator.open, as it's implicitly called in the ctor.
|
|
||||||
tileSources: source,
|
tileSources: source,
|
||||||
tileHost: _this.tileHost,
|
tileHost: _this.tileHost,
|
||||||
prefixUrl: _this.prefixUrl,
|
prefixUrl: _this.prefixUrl,
|
||||||
@ -1164,6 +1171,7 @@ function openTileSource( viewer, source ) {
|
|||||||
viewer: _this
|
viewer: _this
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Instantiate a referencestrip if configured
|
//Instantiate a referencestrip if configured
|
||||||
if ( _this.showReferenceStrip && !_this.referenceStrip ){
|
if ( _this.showReferenceStrip && !_this.referenceStrip ){
|
||||||
@ -1186,7 +1194,7 @@ function openTileSource( viewer, source ) {
|
|||||||
|
|
||||||
THIS[ _this.hash ].animating = false;
|
THIS[ _this.hash ].animating = false;
|
||||||
THIS[ _this.hash ].forceRedraw = true;
|
THIS[ _this.hash ].forceRedraw = true;
|
||||||
THIS[ _this.hash ].updateRequestId = scheduleUpdate( _this, updateMulti );
|
_this._updateRequestId = scheduleUpdate( _this, updateMulti );
|
||||||
|
|
||||||
//Assuming you had programatically created a bunch of overlays
|
//Assuming you had programatically created a bunch of overlays
|
||||||
//and added them via configuration
|
//and added them via configuration
|
||||||
@ -1233,23 +1241,7 @@ function openTileSource( viewer, source ) {
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Schedulers provide the general engine for animation
|
// Schedulers provide the general engine for animation
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
function scheduleUpdate( viewer, updateFunc, prevUpdateTime ){
|
function scheduleUpdate( viewer, updateFunc ){
|
||||||
var currentTime,
|
|
||||||
targetTime,
|
|
||||||
deltaTime;
|
|
||||||
|
|
||||||
if ( THIS[ viewer.hash ].animating ) {
|
|
||||||
return $.requestAnimationFrame( function(){
|
|
||||||
updateFunc( viewer );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTime = +new Date();
|
|
||||||
prevUpdateTime = prevUpdateTime ? prevUpdateTime : currentTime;
|
|
||||||
// 60 frames per second is ideal
|
|
||||||
targetTime = prevUpdateTime + 1000 / 60;
|
|
||||||
deltaTime = Math.max( 1, targetTime - currentTime );
|
|
||||||
|
|
||||||
return $.requestAnimationFrame( function(){
|
return $.requestAnimationFrame( function(){
|
||||||
updateFunc( viewer );
|
updateFunc( viewer );
|
||||||
} );
|
} );
|
||||||
@ -1455,19 +1447,17 @@ function onContainerEnter( tracker, position, buttonDownElement, buttonDownAny )
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function updateMulti( viewer ) {
|
function updateMulti( viewer ) {
|
||||||
|
|
||||||
var beginTime;
|
|
||||||
|
|
||||||
if ( !viewer.source ) {
|
if ( !viewer.source ) {
|
||||||
THIS[ viewer.hash ].updateRequestId = null;
|
viewer._updateRequestId = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
beginTime = +new Date();
|
|
||||||
updateOnce( viewer );
|
updateOnce( viewer );
|
||||||
|
|
||||||
THIS[ viewer.hash ].updateRequestId = scheduleUpdate( viewer,
|
// Request the next frame, unless we've been closed during the updateOnce()
|
||||||
arguments.callee, beginTime );
|
if ( viewer.source ) {
|
||||||
|
viewer._updateRequestId = scheduleUpdate( viewer, updateMulti );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateOnce( viewer ) {
|
function updateOnce( viewer ) {
|
||||||
|
@ -13,8 +13,7 @@
|
|||||||
id: 'example',
|
id: 'example',
|
||||||
prefixUrl: '/build/openseadragon/images/',
|
prefixUrl: '/build/openseadragon/images/',
|
||||||
tileSources: '/test/data/testpattern.dzi',
|
tileSources: '/test/data/testpattern.dzi',
|
||||||
springStiffness: 100, // Faster animation = faster tests
|
springStiffness: 100 // Faster animation = faster tests
|
||||||
showNavigator: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ok(viewer, 'Viewer exists');
|
ok(viewer, 'Viewer exists');
|
||||||
@ -25,6 +24,8 @@
|
|||||||
equal(eventSender, viewer, 'Sender of open event was viewer');
|
equal(eventSender, viewer, 'Sender of open event was viewer');
|
||||||
ok(eventData, 'Handler also received event data');
|
ok(eventData, 'Handler also received event data');
|
||||||
ok(viewer.viewport, 'Viewport exists');
|
ok(viewer.viewport, 'Viewport exists');
|
||||||
|
ok(viewer.source, 'source exists');
|
||||||
|
ok(viewer._updateRequestId, 'timer is on');
|
||||||
start();
|
start();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -123,9 +124,14 @@
|
|||||||
asyncTest('Close', function() {
|
asyncTest('Close', function() {
|
||||||
var closeHandler = function() {
|
var closeHandler = function() {
|
||||||
viewer.removeHandler('close', closeHandler);
|
viewer.removeHandler('close', closeHandler);
|
||||||
|
ok(!viewer.source, 'no source');
|
||||||
$('#example').empty();
|
$('#example').empty();
|
||||||
ok(true, 'Close event was sent');
|
ok(true, 'Close event was sent');
|
||||||
|
ok(!viewer._updateRequestId, 'timer is off');
|
||||||
|
setTimeout(function() {
|
||||||
|
ok(!viewer._updateRequestId, 'timer is still off');
|
||||||
start();
|
start();
|
||||||
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
viewer.addHandler('close', closeHandler);
|
viewer.addHandler('close', closeHandler);
|
||||||
|
@ -461,4 +461,59 @@ QUnit.config.autostart = false;
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('Viewer closing one image and opening another', function() {
|
||||||
|
var timeWatcher = Util.timeWatcher();
|
||||||
|
|
||||||
|
viewer = OpenSeadragon({
|
||||||
|
id: 'example',
|
||||||
|
prefixUrl: '/build/openseadragon/images/',
|
||||||
|
tileSources: '/test/data/testpattern.dzi',
|
||||||
|
springStiffness: 100, // Faster animation = faster tests
|
||||||
|
showNavigator: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var openHandler1 = function(eventSender, eventData) {
|
||||||
|
viewer.removeHandler('open', openHandler1);
|
||||||
|
ok(viewer.navigator, 'navigator exists');
|
||||||
|
viewer.navigator.addHandler('open', navOpenHandler1);
|
||||||
|
};
|
||||||
|
|
||||||
|
var navOpenHandler1 = function(eventSender, eventData) {
|
||||||
|
viewer.navigator.removeHandler('open', navOpenHandler1);
|
||||||
|
equal(viewer.navigator.source, viewer.source, 'viewer and navigator have the same source');
|
||||||
|
ok(viewer.navigator._updateRequestId, 'navigator timer is on');
|
||||||
|
viewer.addHandler('close', closeHandler1);
|
||||||
|
viewer.addHandler('open', openHandler2);
|
||||||
|
viewer.open('/test/data/tall.dzi');
|
||||||
|
};
|
||||||
|
|
||||||
|
var closeHandler1 = function() {
|
||||||
|
viewer.removeHandler('close', closeHandler1);
|
||||||
|
ok(true, 'calling open closes the old one');
|
||||||
|
equal(viewer.navigator.source, null, 'navigator source has been cleared');
|
||||||
|
};
|
||||||
|
|
||||||
|
var openHandler2 = function(eventSender, eventData) {
|
||||||
|
viewer.removeHandler('open', openHandler2);
|
||||||
|
viewer.navigator.addHandler('open', navOpenHandler2);
|
||||||
|
};
|
||||||
|
|
||||||
|
var navOpenHandler2 = function(eventSender, eventData) {
|
||||||
|
viewer.navigator.removeHandler('open', navOpenHandler2);
|
||||||
|
equal(viewer.navigator.source, viewer.source, 'viewer and navigator have the same source');
|
||||||
|
viewer.addHandler('close', closeHandler2);
|
||||||
|
viewer.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
var closeHandler2 = function() {
|
||||||
|
viewer.removeHandler('close', closeHandler2);
|
||||||
|
ok(!viewer.navigator._updateRequestId, 'navigator timer is off');
|
||||||
|
setTimeout(function() {
|
||||||
|
ok(!viewer.navigator._updateRequestId, 'navigator timer is still off');
|
||||||
|
timeWatcher.done();
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
viewer.addHandler('open', openHandler1);
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
Loading…
Reference in New Issue
Block a user