Merge pull request #631 from openseadragon/spring

Now animating zoom spring exponentially
This commit is contained in:
Ian Gilman 2015-03-30 15:03:21 -07:00
commit b92d3f36a8
6 changed files with 169 additions and 10 deletions

View File

@ -56,6 +56,7 @@ OPENSEADRAGON CHANGELOG
* Fixed an issue with zero size viewers in IE8 (#609) * Fixed an issue with zero size viewers in IE8 (#609)
* Fixed: Cross Origin policy not working (#613) * Fixed: Cross Origin policy not working (#613)
* Optimized tile loading by clearing the queue on a re-draw when imageLoaderLimit is set (#616) * Optimized tile loading by clearing the queue on a re-draw when imageLoaderLimit is set (#616)
* Now animating zoom spring exponentially (#631)
1.2.1: 1.2.1:

View File

@ -38,10 +38,14 @@
* @class Spring * @class Spring
* @memberof OpenSeadragon * @memberof OpenSeadragon
* @param {Object} options - Spring configuration settings. * @param {Object} options - Spring configuration settings.
* @param {Number} options.initial - Initial value of spring, default to 0 so * @param {Number} options.springStiffness - Spring stiffness. Must be greater than zero.
* spring is not in motion initally by default. * The closer to zero, the closer to linear animation.
* @param {Number} options.springStiffness - Spring stiffness. * @param {Number} options.animationTime - Animation duration per spring, in seconds.
* @param {Number} options.animationTime - Animation duration per spring. * Must be greater than zero.
* @param {Number} [options.initial=0] - Initial value of spring.
* @param {Boolean} [options.exponential=false] - Whether this spring represents
* an exponential scale (such as zoom) and should be animated accordingly. Note that
* exponential springs must have non-zero values.
*/ */
$.Spring = function( options ) { $.Spring = function( options ) {
var args = arguments; var args = arguments;
@ -52,7 +56,7 @@ $.Spring = function( options ) {
options = { options = {
initial: args.length && typeof ( args[ 0 ] ) == "number" ? initial: args.length && typeof ( args[ 0 ] ) == "number" ?
args[ 0 ] : args[ 0 ] :
0, undefined,
/** /**
* Spring stiffness. * Spring stiffness.
* @member {Number} springStiffness * @member {Number} springStiffness
@ -72,6 +76,17 @@ $.Spring = function( options ) {
}; };
} }
$.console.assert(typeof options.springStiffness === "number" && options.springStiffness !== 0,
"[OpenSeadragon.Spring] options.springStiffness must be a non-zero number");
$.console.assert(typeof options.animationTime === "number" && options.springStiffness !== 0,
"[OpenSeadragon.Spring] options.animationTime must be a non-zero number");
if (options.exponential) {
this._exponential = true;
delete options.exponential;
}
$.extend( true, this, options); $.extend( true, this, options);
/** /**
@ -83,10 +98,13 @@ $.Spring = function( options ) {
this.current = { this.current = {
value: typeof ( this.initial ) == "number" ? value: typeof ( this.initial ) == "number" ?
this.initial : this.initial :
0, (this._exponential ? 0 : 1),
time: $.now() // always work in milliseconds time: $.now() // always work in milliseconds
}; };
$.console.assert(!this._exponential || this.current.value !== 0,
"[OpenSeadragon.Spring] value must be non-zero for exponential springs");
/** /**
* @member {Object} start * @member {Object} start
* @memberof OpenSeadragon.Spring# * @memberof OpenSeadragon.Spring#
@ -108,6 +126,12 @@ $.Spring = function( options ) {
value: this.current.value, value: this.current.value,
time: this.current.time time: this.current.time
}; };
if (this._exponential) {
this.start._logValue = Math.log(this.start.value);
this.target._logValue = Math.log(this.target.value);
this.current._logValue = Math.log(this.current.value);
}
}; };
$.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
@ -117,8 +141,17 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
* @param {Number} target * @param {Number} target
*/ */
resetTo: function( target ) { resetTo: function( target ) {
$.console.assert(!this._exponential || target !== 0,
"[OpenSeadragon.Spring.resetTo] target must be non-zero for exponential springs");
this.start.value = this.target.value = this.current.value = target; this.start.value = this.target.value = this.current.value = target;
this.start.time = this.target.time = this.current.time = $.now(); this.start.time = this.target.time = this.current.time = $.now();
if (this._exponential) {
this.start._logValue = Math.log(this.start.value);
this.target._logValue = Math.log(this.target.value);
this.current._logValue = Math.log(this.current.value);
}
}, },
/** /**
@ -126,10 +159,18 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
* @param {Number} target * @param {Number} target
*/ */
springTo: function( target ) { springTo: function( target ) {
$.console.assert(!this._exponential || target !== 0,
"[OpenSeadragon.Spring.springTo] target must be non-zero for exponential springs");
this.start.value = this.current.value; this.start.value = this.current.value;
this.start.time = this.current.time; this.start.time = this.current.time;
this.target.value = target; this.target.value = target;
this.target.time = this.start.time + 1000 * this.animationTime; this.target.time = this.start.time + 1000 * this.animationTime;
if (this._exponential) {
this.start._logValue = Math.log(this.start.value);
this.target._logValue = Math.log(this.target.value);
}
}, },
/** /**
@ -139,6 +180,27 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
shiftBy: function( delta ) { shiftBy: function( delta ) {
this.start.value += delta; this.start.value += delta;
this.target.value += delta; this.target.value += delta;
if (this._exponential) {
$.console.assert(this.target.value !== 0 && this.start.value !== 0,
"[OpenSeadragon.Spring.shiftBy] spring value must be non-zero for exponential springs");
this.start._logValue = Math.log(this.start.value);
this.target._logValue = Math.log(this.target.value);
}
},
setExponential: function(value) {
this._exponential = value;
if (this._exponential) {
$.console.assert(this.current.value !== 0 && this.target.value !== 0 && this.start.value !== 0,
"[OpenSeadragon.Spring.setExponential] spring value must be non-zero for exponential springs");
this.start._logValue = Math.log(this.start.value);
this.target._logValue = Math.log(this.target.value);
this.current._logValue = Math.log(this.current.value);
}
}, },
/** /**
@ -146,15 +208,31 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
*/ */
update: function() { update: function() {
this.current.time = $.now(); this.current.time = $.now();
this.current.value = (this.current.time >= this.target.time) ?
this.target.value : var startValue, targetValue;
this.start.value + if (this._exponential) {
( this.target.value - this.start.value ) * startValue = this.start._logValue;
targetValue = this.target._logValue;
} else {
startValue = this.start.value;
targetValue = this.target.value;
}
var currentValue = (this.current.time >= this.target.time) ?
targetValue :
startValue +
( targetValue - startValue ) *
transform( transform(
this.springStiffness, this.springStiffness,
( this.current.time - this.start.time ) / ( this.current.time - this.start.time ) /
( this.target.time - this.start.time ) ( this.target.time - this.start.time )
); );
if (this._exponential) {
this.current.value = Math.exp(currentValue);
} else {
this.current.value = currentValue;
}
} }
}; };

View File

@ -127,6 +127,7 @@ $.Viewport = function( options ) {
animationTime: this.animationTime animationTime: this.animationTime
}); });
this.zoomSpring = new $.Spring({ this.zoomSpring = new $.Spring({
exponential: true,
initial: 1, initial: 1,
springStiffness: this.springStiffness, springStiffness: this.springStiffness,
animationTime: this.animationTime animationTime: this.animationTime

View File

@ -72,6 +72,7 @@
<script src="/test/modules/tilecache.js"></script> <script src="/test/modules/tilecache.js"></script>
<script src="/test/modules/referencestrip.js"></script> <script src="/test/modules/referencestrip.js"></script>
<script src="/test/modules/tilesourcecollection.js"></script> <script src="/test/modules/tilesourcecollection.js"></script>
<script src="/test/modules/spring.js"></script>
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up) <!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
so we put them last. --> so we put them last. -->
<script src="/test/modules/navigator.js"></script> <script src="/test/modules/navigator.js"></script>

77
test/modules/spring.js Normal file
View File

@ -0,0 +1,77 @@
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog, propEqual, console */
(function () {
var originalNow;
var now;
module("spring", {
setup: function () {
now = 0;
originalNow = OpenSeadragon.now;
OpenSeadragon.now = function() {
return now;
};
},
teardown: function () {
OpenSeadragon.now = originalNow;
}
});
asyncTest('regular spring', function() {
var spring = new OpenSeadragon.Spring({
initial: 5,
animationTime: 1,
springStiffness: 0.000001
});
equal(spring.current.value, 5, 'initial current value');
equal(spring.target.value, 5, 'initial target value');
spring.springTo(6);
equal(spring.current.value, 5, 'current value after springTo');
equal(spring.target.value, 6, 'target value after springTo');
now = 500;
spring.update();
Util.assessNumericValue(5.5, spring.current.value, 0.00001, 'current value after first update');
equal(spring.target.value, 6, 'target value after first update');
now = 1000;
spring.update();
equal(spring.current.value, 6, 'current value after second update');
equal(spring.target.value, 6, 'target value after second update');
start();
});
asyncTest('exponential spring', function() {
var spring = new OpenSeadragon.Spring({
exponential: true,
initial: 1,
animationTime: 1,
springStiffness: 0.000001
});
equal(spring.current.value, 1, 'initial current value');
equal(spring.target.value, 1, 'initial target value');
spring.springTo(2);
equal(spring.current.value, 1, 'current value after springTo');
equal(spring.target.value, 2, 'target value after springTo');
now = 500;
spring.update();
Util.assessNumericValue(1.41421, spring.current.value, 0.00001, 'current value after first update');
equal(spring.target.value, 2, 'target value after first update');
now = 1000;
spring.update();
equal(spring.current.value, 2, 'current value after second update');
equal(spring.target.value, 2, 'target value after second update');
start();
});
})();

View File

@ -37,6 +37,7 @@
<script src="/test/modules/tilecache.js"></script> <script src="/test/modules/tilecache.js"></script>
<script src="/test/modules/referencestrip.js"></script> <script src="/test/modules/referencestrip.js"></script>
<script src="/test/modules/tilesourcecollection.js"></script> <script src="/test/modules/tilesourcecollection.js"></script>
<script src="/test/modules/spring.js"></script>
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up) <!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
so we put them last. --> so we put them last. -->
<script src="/test/modules/navigator.js"></script> <script src="/test/modules/navigator.js"></script>