From b395f1793d1bc50a7a9656c2a4d3a5cef1d145f3 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 17 Mar 2015 16:43:25 -0700 Subject: [PATCH 1/3] Logarithmic option for springs; used for zoomSpring --- src/spring.js | 61 +++++++++++++++++++++++++++++++++++++++++++++---- src/viewport.js | 1 + 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/spring.js b/src/spring.js index 3320ecc2..466cf15b 100644 --- a/src/spring.js +++ b/src/spring.js @@ -72,6 +72,11 @@ $.Spring = function( options ) { }; } + if (options.log) { + this._log = true; + delete options.log; + } + $.extend( true, this, options); /** @@ -108,6 +113,12 @@ $.Spring = function( options ) { value: this.current.value, time: this.current.time }; + + if (this._log) { + 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 */{ @@ -119,6 +130,12 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ resetTo: function( target ) { this.start.value = this.target.value = this.current.value = target; this.start.time = this.target.time = this.current.time = $.now(); + + if (this._log) { + this.start._logValue = Math.log(this.start.value); + this.target._logValue = Math.log(this.target.value); + this.current._logValue = Math.log(this.current.value); + } }, /** @@ -130,6 +147,11 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ this.start.time = this.current.time; this.target.value = target; this.target.time = this.start.time + 1000 * this.animationTime; + + if (this._log) { + this.start._logValue = Math.log(this.start.value); + this.target._logValue = Math.log(this.target.value); + } }, /** @@ -139,6 +161,21 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ shiftBy: function( delta ) { this.start.value += delta; this.target.value += delta; + + if (this._log) { + this.start._logValue = Math.log(this.start.value); + this.target._logValue = Math.log(this.target.value); + } + }, + + setLog: function(value) { + this._log = value; + + if (this._log) { + 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 +183,31 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ */ update: function() { this.current.time = $.now(); - this.current.value = (this.current.time >= this.target.time) ? - this.target.value : - this.start.value + - ( this.target.value - this.start.value ) * + + var startValue, targetValue; + if (this._log) { + 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( this.springStiffness, ( this.current.time - this.start.time ) / ( this.target.time - this.start.time ) ); + + if (this._log) { + this.current.value = Math.exp(currentValue); + } else { + this.current.value = currentValue; + } } }; diff --git a/src/viewport.js b/src/viewport.js index ff91ad29..6b246f37 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -127,6 +127,7 @@ $.Viewport = function( options ) { animationTime: this.animationTime }); this.zoomSpring = new $.Spring({ + log: true, initial: 1, springStiffness: this.springStiffness, animationTime: this.animationTime From 332b28ab2d7aaef338d7e5752008bdfe16a11710 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 30 Mar 2015 14:50:37 -0700 Subject: [PATCH 2/3] * Now animating zoom spring exponentially --- changelog.txt | 1 + src/spring.js | 61 +++++++++++++++++++++++---------- src/viewport.js | 2 +- test/coverage.html | 1 + test/modules/spring.js | 77 ++++++++++++++++++++++++++++++++++++++++++ test/test.html | 1 + 6 files changed, 124 insertions(+), 19 deletions(-) create mode 100644 test/modules/spring.js diff --git a/changelog.txt b/changelog.txt index e416df23..ee87232e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -56,6 +56,7 @@ OPENSEADRAGON CHANGELOG * Fixed an issue with zero size viewers in IE8 (#609) * Fixed: Cross Origin policy not working (#613) * Optimized tile loading by clearing the queue on a re-draw when imageLoaderLimit is set (#616) +* Now animating zoom spring exponentially 1.2.1: diff --git a/src/spring.js b/src/spring.js index 466cf15b..2c89ee07 100644 --- a/src/spring.js +++ b/src/spring.js @@ -38,10 +38,14 @@ * @class Spring * @memberof OpenSeadragon * @param {Object} options - Spring configuration settings. - * @param {Number} options.initial - Initial value of spring, default to 0 so - * spring is not in motion initally by default. - * @param {Number} options.springStiffness - Spring stiffness. - * @param {Number} options.animationTime - Animation duration per spring. + * @param {Number} options.springStiffness - Spring stiffness. Must be greater than zero. + * The closer to zero, the closer to linear animation. + * @param {Number} options.animationTime - Animation duration per spring, in seconds. + * 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 ) { var args = arguments; @@ -52,7 +56,7 @@ $.Spring = function( options ) { options = { initial: args.length && typeof ( args[ 0 ] ) == "number" ? args[ 0 ] : - 0, + undefined, /** * Spring stiffness. * @member {Number} springStiffness @@ -72,9 +76,15 @@ $.Spring = function( options ) { }; } - if (options.log) { - this._log = true; - delete options.log; + $.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); @@ -88,10 +98,13 @@ $.Spring = function( options ) { this.current = { value: typeof ( this.initial ) == "number" ? this.initial : - 0, + (this._exponential ? 0 : 1), 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 * @memberof OpenSeadragon.Spring# @@ -114,7 +127,7 @@ $.Spring = function( options ) { time: this.current.time }; - if (this._log) { + 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); @@ -128,10 +141,13 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ * @param {Number} 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.time = this.target.time = this.current.time = $.now(); - if (this._log) { + 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); @@ -143,12 +159,15 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ * @param {Number} 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.time = this.current.time; this.target.value = target; this.target.time = this.start.time + 1000 * this.animationTime; - if (this._log) { + if (this._exponential) { this.start._logValue = Math.log(this.start.value); this.target._logValue = Math.log(this.target.value); } @@ -162,16 +181,22 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ this.start.value += delta; this.target.value += delta; - if (this._log) { + 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); } }, - setLog: function(value) { - this._log = 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"); - if (this._log) { this.start._logValue = Math.log(this.start.value); this.target._logValue = Math.log(this.target.value); this.current._logValue = Math.log(this.current.value); @@ -185,7 +210,7 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ this.current.time = $.now(); var startValue, targetValue; - if (this._log) { + if (this._exponential) { startValue = this.start._logValue; targetValue = this.target._logValue; } else { @@ -203,7 +228,7 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{ ( this.target.time - this.start.time ) ); - if (this._log) { + if (this._exponential) { this.current.value = Math.exp(currentValue); } else { this.current.value = currentValue; diff --git a/src/viewport.js b/src/viewport.js index 6b246f37..b882580e 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -127,7 +127,7 @@ $.Viewport = function( options ) { animationTime: this.animationTime }); this.zoomSpring = new $.Spring({ - log: true, + exponential: true, initial: 1, springStiffness: this.springStiffness, animationTime: this.animationTime diff --git a/test/coverage.html b/test/coverage.html index fe75c797..9d61c48f 100644 --- a/test/coverage.html +++ b/test/coverage.html @@ -72,6 +72,7 @@ + diff --git a/test/modules/spring.js b/test/modules/spring.js new file mode 100644 index 00000000..b627f832 --- /dev/null +++ b/test/modules/spring.js @@ -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(); + }); + +})(); diff --git a/test/test.html b/test/test.html index f1f1716c..ae4863e8 100644 --- a/test/test.html +++ b/test/test.html @@ -37,6 +37,7 @@ + From bc83f609048d6a794372416a4d4cb45beafadd3a Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 30 Mar 2015 14:54:25 -0700 Subject: [PATCH 3/3] Changelog for #631 --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index ee87232e..5ff344fb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -56,7 +56,7 @@ OPENSEADRAGON CHANGELOG * Fixed an issue with zero size viewers in IE8 (#609) * Fixed: Cross Origin policy not working (#613) * Optimized tile loading by clearing the queue on a re-draw when imageLoaderLimit is set (#616) -* Now animating zoom spring exponentially +* Now animating zoom spring exponentially (#631) 1.2.1: