troika-animation
Advanced tools
Comparing version 0.34.0 to 0.35.0
@@ -0,0 +0,0 @@ import {number, color} from '../src/Interpolators.js' |
@@ -0,0 +0,0 @@ import MultiTween from '../src/MultiTween.js' |
@@ -0,0 +0,0 @@ import Tween from '../src/Tween.js' |
@@ -6,2 +6,13 @@ # Change Log | ||
# [0.35.0](https://github.com/protectwise/troika/compare/v0.34.2...v0.35.0) (2020-11-16) | ||
### Features | ||
* initial support for spring physics-based transitions ([5e05bc8](https://github.com/protectwise/troika/commit/5e05bc8b0f2d0dd7af1f5f59f41c60929ac45ae2)) | ||
# [0.34.0](https://github.com/protectwise/troika/compare/v0.33.1...v0.34.0) (2020-10-19) | ||
@@ -8,0 +19,0 @@ |
@@ -326,2 +326,31 @@ /* | ||
/** | ||
* @interface AbstractTween | ||
* Defines the interface expected by `Runner` for tween-like things. | ||
*/ | ||
class AbstractTween { | ||
/** | ||
* @abstract | ||
* For a given elapsed time relative to the start of the tween, calculates the value at that time and calls the | ||
* `callback` function with that value. If the given time is during the `delay` period, the callback will not be | ||
* invoked. | ||
* @param {number} time | ||
*/ | ||
gotoElapsedTime(time) {} | ||
/** | ||
* @abstract | ||
* Like `gotoElapsedTime` but goes to the very end of the tween. | ||
*/ | ||
gotoEnd() {} | ||
/** | ||
* @abstract | ||
* For a given elapsed time relative to the start of the tween, determines if the tween is in its completed end state. | ||
* @param {number} time | ||
* @return {boolean} | ||
*/ | ||
isDoneAtElapsedTime(time) {} | ||
} | ||
const linear$1 = v => v; | ||
@@ -354,4 +383,5 @@ const maxSafeInteger = 0x1fffffffffffff; | ||
*/ | ||
class Tween { | ||
class Tween extends AbstractTween { | ||
constructor(callback, fromValue, toValue, duration=750, delay=0, easing=linear$1, iterations=1, direction='forward', interpolate='number') { | ||
super(); | ||
this.callback = callback; | ||
@@ -404,2 +434,11 @@ this.fromValue = fromValue; | ||
} | ||
/** | ||
* For a given elapsed time relative to the start of the tween, determines if the tween is in its completed end state. | ||
* @param {number} time | ||
* @return {boolean} | ||
*/ | ||
isDoneAtElapsedTime(time) { | ||
return time > this.totalElapsed | ||
} | ||
} | ||
@@ -599,3 +638,3 @@ | ||
// Queue for removal if we're past its end time | ||
if (elapsed > tween.totalElapsed) { | ||
if (tween.isDoneAtElapsedTime(elapsed)) { | ||
this.stop(tween); | ||
@@ -647,2 +686,117 @@ if (tween.onDone) { | ||
export { Easings, Interpolators, MultiTween, Runner, Tween, setAnimationScheduler }; | ||
/** | ||
* Preset spring physics configurations. | ||
* For convenience, these match the presets defined by react-spring: https://www.react-spring.io/docs/hooks/api | ||
*/ | ||
var PRESETS = { | ||
default: { mass: 1, tension: 170, friction: 26 }, | ||
gentle: { mass: 1, tension: 120, friction: 14 }, | ||
wobbly: { mass: 1, tension: 180, friction: 12 }, | ||
stiff: { mass: 1, tension: 210, friction: 20 }, | ||
slow: { mass: 1, tension: 280, friction: 60 }, | ||
molasses: { mass: 1, tension: 280, friction: 120 } | ||
}; | ||
// Factors to be applied to the tension and friction values; these match those used by | ||
// react-spring internally, so that users can use the same spring configs as they would | ||
// in react-spring. | ||
const tensionFactor = 0.000001; | ||
const frictionFactor = 0.001; | ||
const DEFAULTS = PRESETS.default; | ||
/** | ||
* @class SpringTween | ||
* Represents a transition between two values based on spring physics. | ||
* | ||
* This is very similar to `Tween`, except that it does not have a fixed duration. Instead, it advances a simple | ||
* spring physics simulation on each call to `gotoElapsedTime`. Since it depends on being advanced in forward-time | ||
* order, it cannot be repeated or run in a reverse direction. It is also not usable as a member of a `MultiTween`. | ||
* | ||
* The `toValue` property can be modified at any time while the simulation is running, and the velocity will be | ||
* maintained; this makes spring tweens more useful than duration-based tweens for objects whose target values are | ||
* changed rapidly over time, e.g. drag-drop. | ||
* | ||
* Non-numeric interpolations are not yet supported. | ||
* | ||
* @param callback {Function} a function that will be called with the current tween value at a given point in time. | ||
* @param {number} fromValue - the beginning value | ||
* @param {number} toValue - the initial ending value; this can be modified later by setting the `toValue` property | ||
* @param {string|object} springConfig - the physical configuration of the spring physics simulation. Either an object | ||
* with `mass`, `tension`, and `friction` properties, or a string corresponding to one of the presets defined | ||
* in `SpringPresets.js`. Defaults to the "default" preset. | ||
* @param {number} springConfig.mass - the mass of the simulated object being moved | ||
* @param {number} springConfig.tension - the spring's tension constant accelerating the simulated object | ||
* @param {number} springConfig.friction - the friction force decelerating the simulated object | ||
* @param {number} [initialVelocity] - velocity of the object at the start of the simulation | ||
* @param {number} [delay] optional time in milliseconds to wait before starting the simulation | ||
*/ | ||
class SpringTween extends AbstractTween { | ||
constructor ( | ||
callback, | ||
fromValue, | ||
toValue, | ||
springConfig, | ||
initialVelocity = 0, | ||
delay = 0 | ||
) { | ||
super(); | ||
this.isSpring = true; | ||
this.callback = callback; | ||
this.currentValue = fromValue; | ||
this.toValue = toValue; | ||
this.velocity = initialVelocity; | ||
this.delay = delay; | ||
if (typeof springConfig === 'string') { | ||
springConfig = PRESETS[springConfig]; | ||
} | ||
if (!springConfig) springConfig = DEFAULTS; | ||
const {mass, tension, friction} = springConfig; | ||
this.mass = typeof mass === 'number' ? mass : DEFAULTS.mass; | ||
this.tension = (typeof tension === 'number' ? tension : DEFAULTS.tension) * tensionFactor; | ||
this.friction = (typeof friction === 'number' ? friction : DEFAULTS.friction) * frictionFactor; | ||
this.minAcceleration = 1e-10; // in units/ms^2 - TODO make this configurable | ||
this.$lastTime = delay; | ||
this.$endTime = Infinity; //unknown until simulation is stepped to the end state | ||
} | ||
gotoElapsedTime (time) { | ||
if (time >= this.delay) { | ||
let { toValue, mass, tension, friction, minAcceleration } = this; | ||
let velocity = this.velocity || 0; | ||
let value = this.currentValue; | ||
// Step simulation by 1ms | ||
for (let t = this.$lastTime; t < time; t++) { | ||
const acceleration = (tension * (toValue - value) - friction * velocity) / mass; | ||
// Acceleration converges to zero near end state | ||
if (Math.abs(acceleration) < minAcceleration) { | ||
velocity = 0; | ||
value = toValue; | ||
this.$endTime = t; | ||
break | ||
} else { | ||
velocity += acceleration; | ||
value += velocity; | ||
} | ||
} | ||
this.velocity = velocity; | ||
this.$lastTime = time; | ||
this.callback(this.currentValue = value); | ||
} | ||
} | ||
gotoEnd () { | ||
this.velocity = 0; | ||
this.$lastTime = this.$endTime; | ||
this.callback(this.currentValue = this.toValue); | ||
} | ||
isDoneAtElapsedTime (time) { | ||
return time >= this.$endTime | ||
} | ||
} | ||
export { Easings, Interpolators, MultiTween, Runner, SpringTween, Tween, setAnimationScheduler }; |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.troika_animation = {})); | ||
(global = global || self, factory(global.troika_animation = {})); | ||
}(this, (function (exports) { 'use strict'; | ||
@@ -323,2 +323,24 @@ | ||
/** | ||
* @interface AbstractTween | ||
* Defines the interface expected by `Runner` for tween-like things. | ||
*/ | ||
var AbstractTween = function AbstractTween () {}; | ||
AbstractTween.prototype.gotoElapsedTime = function gotoElapsedTime (time) {}; | ||
/** | ||
* @abstract | ||
* Like `gotoElapsedTime` but goes to the very end of the tween. | ||
*/ | ||
AbstractTween.prototype.gotoEnd = function gotoEnd () {}; | ||
/** | ||
* @abstract | ||
* For a given elapsed time relative to the start of the tween, determines if the tween is in its completed end state. | ||
* @param {number} time | ||
* @return {boolean} | ||
*/ | ||
AbstractTween.prototype.isDoneAtElapsedTime = function isDoneAtElapsedTime (time) {}; | ||
var linear$1 = function (v) { return v; }; | ||
@@ -351,58 +373,76 @@ var maxSafeInteger = 0x1fffffffffffff; | ||
*/ | ||
var Tween = function Tween(callback, fromValue, toValue, duration, delay, easing, iterations, direction, interpolate) { | ||
if ( duration === void 0 ) duration=750; | ||
if ( delay === void 0 ) delay=0; | ||
if ( easing === void 0 ) easing=linear$1; | ||
if ( iterations === void 0 ) iterations=1; | ||
if ( direction === void 0 ) direction='forward'; | ||
if ( interpolate === void 0 ) interpolate='number'; | ||
var Tween = /*@__PURE__*/(function (AbstractTween) { | ||
function Tween(callback, fromValue, toValue, duration, delay, easing, iterations, direction, interpolate) { | ||
if ( duration === void 0 ) duration=750; | ||
if ( delay === void 0 ) delay=0; | ||
if ( easing === void 0 ) easing=linear$1; | ||
if ( iterations === void 0 ) iterations=1; | ||
if ( direction === void 0 ) direction='forward'; | ||
if ( interpolate === void 0 ) interpolate='number'; | ||
this.callback = callback; | ||
this.fromValue = fromValue; | ||
this.toValue = toValue; | ||
this.duration = duration; | ||
this.delay = delay; | ||
this.easing = typeof easing === 'string' ? (Easings[easing] || linear$1) : easing; | ||
this.iterations = iterations; | ||
this.direction = direction; | ||
this.interpolate = typeof interpolate === 'function' ? interpolate : Interpolators[interpolate] || number; | ||
AbstractTween.call(this); | ||
this.callback = callback; | ||
this.fromValue = fromValue; | ||
this.toValue = toValue; | ||
this.duration = duration; | ||
this.delay = delay; | ||
this.easing = typeof easing === 'string' ? (Easings[easing] || linear$1) : easing; | ||
this.iterations = iterations; | ||
this.direction = direction; | ||
this.interpolate = typeof interpolate === 'function' ? interpolate : Interpolators[interpolate] || number; | ||
/** | ||
* @property totalElapsed | ||
* @type {number} | ||
* The total duration of this tween from 0 to its completion, taking into account its `duration`, `delay`, and | ||
* `iterations`. This is calculated once upon instantiation, and may be used to determine whether the tween is | ||
* finished or not at a given time. | ||
*/ | ||
this.totalElapsed = this.iterations < maxSafeInteger ? this.delay + (this.duration * this.iterations) : maxSafeInteger; | ||
} | ||
if ( AbstractTween ) Tween.__proto__ = AbstractTween; | ||
Tween.prototype = Object.create( AbstractTween && AbstractTween.prototype ); | ||
Tween.prototype.constructor = Tween; | ||
/** | ||
* @property totalElapsed | ||
* @type {number} | ||
* The total duration of this tween from 0 to its completion, taking into account its `duration`, `delay`, and | ||
* `iterations`. This is calculated once upon instantiation, and may be used to determine whether the tween is | ||
* finished or not at a given time. | ||
* For a given elapsed time relative to the start of the tween, calculates the value at that time and calls the | ||
* `callback` function with that value. If the given time is during the `delay` period, the callback will not be | ||
* invoked. | ||
* @param {number} time | ||
*/ | ||
this.totalElapsed = this.iterations < maxSafeInteger ? this.delay + (this.duration * this.iterations) : maxSafeInteger; | ||
}; | ||
/** | ||
* For a given elapsed time relative to the start of the tween, calculates the value at that time and calls the | ||
* `callback` function with that value. If the given time is during the `delay` period, the callback will not be | ||
* invoked. | ||
* @param {number} time | ||
*/ | ||
Tween.prototype.gotoElapsedTime = function gotoElapsedTime (time) { | ||
var duration = this.duration; | ||
var delay = this.delay; | ||
if (time >= delay) { | ||
time = Math.min(time, this.totalElapsed) - delay; //never go past final value | ||
var progress = (time % duration) / duration; | ||
if (progress === 0 && time !== 0) { progress = 1; } | ||
progress = this.easing(progress); | ||
if (this.direction === 'reverse' || (this.direction === 'alternate' && Math.ceil(time / duration) % 2 === 0)) { | ||
progress = 1 - progress; | ||
Tween.prototype.gotoElapsedTime = function gotoElapsedTime (time) { | ||
var duration = this.duration; | ||
var delay = this.delay; | ||
if (time >= delay) { | ||
time = Math.min(time, this.totalElapsed) - delay; //never go past final value | ||
var progress = (time % duration) / duration; | ||
if (progress === 0 && time !== 0) { progress = 1; } | ||
progress = this.easing(progress); | ||
if (this.direction === 'reverse' || (this.direction === 'alternate' && Math.ceil(time / duration) % 2 === 0)) { | ||
progress = 1 - progress; | ||
} | ||
this.callback(this.interpolate(this.fromValue, this.toValue, progress)); | ||
} | ||
this.callback(this.interpolate(this.fromValue, this.toValue, progress)); | ||
} | ||
}; | ||
}; | ||
/** | ||
* Like `gotoElapsedTime` but goes to the very end of the tween. | ||
*/ | ||
Tween.prototype.gotoEnd = function gotoEnd () { | ||
this.gotoElapsedTime(this.totalElapsed); | ||
}; | ||
/** | ||
* Like `gotoElapsedTime` but goes to the very end of the tween. | ||
*/ | ||
Tween.prototype.gotoEnd = function gotoEnd () { | ||
this.gotoElapsedTime(this.totalElapsed); | ||
}; | ||
/** | ||
* For a given elapsed time relative to the start of the tween, determines if the tween is in its completed end state. | ||
* @param {number} time | ||
* @return {boolean} | ||
*/ | ||
Tween.prototype.isDoneAtElapsedTime = function isDoneAtElapsedTime (time) { | ||
return time > this.totalElapsed | ||
}; | ||
return Tween; | ||
}(AbstractTween)); | ||
/** | ||
@@ -605,3 +645,3 @@ * A specialized Tween that controls one or more other tweens. The controlled tweens are treated as a | ||
// Queue for removal if we're past its end time | ||
if (elapsed > tween.totalElapsed) { | ||
if (tween.isDoneAtElapsedTime(elapsed)) { | ||
this.stop(tween); | ||
@@ -652,2 +692,133 @@ if (tween.onDone) { | ||
/** | ||
* Preset spring physics configurations. | ||
* For convenience, these match the presets defined by react-spring: https://www.react-spring.io/docs/hooks/api | ||
*/ | ||
var PRESETS = { | ||
default: { mass: 1, tension: 170, friction: 26 }, | ||
gentle: { mass: 1, tension: 120, friction: 14 }, | ||
wobbly: { mass: 1, tension: 180, friction: 12 }, | ||
stiff: { mass: 1, tension: 210, friction: 20 }, | ||
slow: { mass: 1, tension: 280, friction: 60 }, | ||
molasses: { mass: 1, tension: 280, friction: 120 } | ||
}; | ||
// Factors to be applied to the tension and friction values; these match those used by | ||
// react-spring internally, so that users can use the same spring configs as they would | ||
// in react-spring. | ||
var tensionFactor = 0.000001; | ||
var frictionFactor = 0.001; | ||
var DEFAULTS = PRESETS.default; | ||
/** | ||
* @class SpringTween | ||
* Represents a transition between two values based on spring physics. | ||
* | ||
* This is very similar to `Tween`, except that it does not have a fixed duration. Instead, it advances a simple | ||
* spring physics simulation on each call to `gotoElapsedTime`. Since it depends on being advanced in forward-time | ||
* order, it cannot be repeated or run in a reverse direction. It is also not usable as a member of a `MultiTween`. | ||
* | ||
* The `toValue` property can be modified at any time while the simulation is running, and the velocity will be | ||
* maintained; this makes spring tweens more useful than duration-based tweens for objects whose target values are | ||
* changed rapidly over time, e.g. drag-drop. | ||
* | ||
* Non-numeric interpolations are not yet supported. | ||
* | ||
* @param callback {Function} a function that will be called with the current tween value at a given point in time. | ||
* @param {number} fromValue - the beginning value | ||
* @param {number} toValue - the initial ending value; this can be modified later by setting the `toValue` property | ||
* @param {string|object} springConfig - the physical configuration of the spring physics simulation. Either an object | ||
* with `mass`, `tension`, and `friction` properties, or a string corresponding to one of the presets defined | ||
* in `SpringPresets.js`. Defaults to the "default" preset. | ||
* @param {number} springConfig.mass - the mass of the simulated object being moved | ||
* @param {number} springConfig.tension - the spring's tension constant accelerating the simulated object | ||
* @param {number} springConfig.friction - the friction force decelerating the simulated object | ||
* @param {number} [initialVelocity] - velocity of the object at the start of the simulation | ||
* @param {number} [delay] optional time in milliseconds to wait before starting the simulation | ||
*/ | ||
var SpringTween = /*@__PURE__*/(function (AbstractTween) { | ||
function SpringTween ( | ||
callback, | ||
fromValue, | ||
toValue, | ||
springConfig, | ||
initialVelocity, | ||
delay | ||
) { | ||
if ( initialVelocity === void 0 ) initialVelocity = 0; | ||
if ( delay === void 0 ) delay = 0; | ||
AbstractTween.call(this); | ||
this.isSpring = true; | ||
this.callback = callback; | ||
this.currentValue = fromValue; | ||
this.toValue = toValue; | ||
this.velocity = initialVelocity; | ||
this.delay = delay; | ||
if (typeof springConfig === 'string') { | ||
springConfig = PRESETS[springConfig]; | ||
} | ||
if (!springConfig) { springConfig = DEFAULTS; } | ||
var mass = springConfig.mass; | ||
var tension = springConfig.tension; | ||
var friction = springConfig.friction; | ||
this.mass = typeof mass === 'number' ? mass : DEFAULTS.mass; | ||
this.tension = (typeof tension === 'number' ? tension : DEFAULTS.tension) * tensionFactor; | ||
this.friction = (typeof friction === 'number' ? friction : DEFAULTS.friction) * frictionFactor; | ||
this.minAcceleration = 1e-10; // in units/ms^2 - TODO make this configurable | ||
this.$lastTime = delay; | ||
this.$endTime = Infinity; //unknown until simulation is stepped to the end state | ||
} | ||
if ( AbstractTween ) SpringTween.__proto__ = AbstractTween; | ||
SpringTween.prototype = Object.create( AbstractTween && AbstractTween.prototype ); | ||
SpringTween.prototype.constructor = SpringTween; | ||
SpringTween.prototype.gotoElapsedTime = function gotoElapsedTime (time) { | ||
if (time >= this.delay) { | ||
var ref = this; | ||
var toValue = ref.toValue; | ||
var mass = ref.mass; | ||
var tension = ref.tension; | ||
var friction = ref.friction; | ||
var minAcceleration = ref.minAcceleration; | ||
var velocity = this.velocity || 0; | ||
var value = this.currentValue; | ||
// Step simulation by 1ms | ||
for (var t = this.$lastTime; t < time; t++) { | ||
var acceleration = (tension * (toValue - value) - friction * velocity) / mass; | ||
// Acceleration converges to zero near end state | ||
if (Math.abs(acceleration) < minAcceleration) { | ||
velocity = 0; | ||
value = toValue; | ||
this.$endTime = t; | ||
break | ||
} else { | ||
velocity += acceleration; | ||
value += velocity; | ||
} | ||
} | ||
this.velocity = velocity; | ||
this.$lastTime = time; | ||
this.callback(this.currentValue = value); | ||
} | ||
}; | ||
SpringTween.prototype.gotoEnd = function gotoEnd () { | ||
this.velocity = 0; | ||
this.$lastTime = this.$endTime; | ||
this.callback(this.currentValue = this.toValue); | ||
}; | ||
SpringTween.prototype.isDoneAtElapsedTime = function isDoneAtElapsedTime (time) { | ||
return time >= this.$endTime | ||
}; | ||
return SpringTween; | ||
}(AbstractTween)); | ||
exports.Easings = Easings; | ||
@@ -657,2 +828,3 @@ exports.Interpolators = Interpolators; | ||
exports.Runner = Runner; | ||
exports.SpringTween = SpringTween; | ||
exports.Tween = Tween; | ||
@@ -659,0 +831,0 @@ exports.setAnimationScheduler = setAnimationScheduler; |
@@ -1,12 +0,15 @@ | ||
'use strict';(function(f,m){"object"===typeof exports&&"undefined"!==typeof module?m(exports):"function"===typeof define&&define.amd?define(["exports"],m):(f="undefined"!==typeof globalThis?globalThis:f||self,m(f.troika_animation={}))})(this,function(f){function m(a,b){return function(c){return.5>c?.5*a(2*c):.5*b(2*c-1)+.5}}function t(a){return function(b){return k(b,a)}}function u(a){return function(b){return 1-k(1-b,a)}}function v(a){return function(b){return.5>b?.5*k(2*b,a):.5*(1-k(1-(2*b-1),a))+ | ||
.5}}function r(a,b,c){return a+(b-a)*c}function N(a,b){return a.totalElapsed-b.totalElapsed}function O(){}function P(a){return a.runner$running}function Q(a){return!a.runner$stopped}function R(){var a=Date.now();n=null;w&&(p=p.filter(P),w=!1);if(p.length){for(var b=p.length;0<b--;)p[b]._tick(a);y()}}function y(){n||(n=x.requestAnimationFrame(R))}var k=Math.pow,z=Math.PI,D=Math.sqrt,E=z/2,S=2*z,q=t(2),A=u(2),d=v(2),T=t(3),U=u(3),V=v(3),W=t(4),X=u(4),Y=v(4),Z=t(5),aa=u(5),ba=v(5),F=function(a){return 1- | ||
D(1-a*a)},G=function(a){return D(1-k(a-1,2))},ca=m(F,G),H=function(a){return 0===a||1===a?a:1-B(1-a)},B=function(a){return 0===a||1===a?a:Math.pow(2,-10*a)*Math.sin((a-.075)*S/.3)+1},da=m(H,B),I=function(a){return 1-C(1-a)},C=function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},ea=m(I,C),J=Object.freeze({__proto__:null,linear:function(a){return a},easeInQuad:q,easeOutQuad:A,easeInOutQuad:d,easeInCubic:T, | ||
easeOutCubic:U,easeInOutCubic:V,easeInQuart:W,easeOutQuart:X,easeInOutQuart:Y,easeInQuint:Z,easeOutQuint:aa,easeInOutQuint:ba,easeInSine:function(a){return 1-Math.cos(a*E)},easeOutSine:function(a){return Math.sin(a*E)},easeInOutSine:function(a){return-.5*(Math.cos(z*a)-1)},easeInExpo:function(a){return 0===a?0:k(2,10*(a-1))},easeOutExpo:function(a){return 1===a?1:1-k(2,-10*a)},easeInOutExpo:function(a){return 0===a||1===a?a:.5>a?.5*k(2,10*(2*a-1)):.5*(1-k(2,-10*(2*a-1)))+.5},easeInCirc:F,easeOutCirc:G, | ||
easeInOutCirc:ca,easeInElastic:H,easeOutElastic:B,easeInOutElastic:da,easeInBack:function(a){return a*a*(2.70158*a-1.70158)},easeOutBack:function(a){return--a*a*(2.70158*a+1.70158)+1},easeInOutBack:function(a){return 1>(a*=2)?.5*a*a*(3.5949095*a-2.5949095):.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)},easeInBounce:I,easeOutBounce:C,easeInOutBounce:ea}),K=function(){var a,b,c=Object.create(null),g=0;return function(e){if("number"===typeof e)return e;if("string"===typeof e){if(e in c)return c[e];a||(a=document.createElement("canvas"), | ||
b=a.getContext("2d"));a.width=a.height=1;b.fillStyle=e;b.fillRect(0,0,1,1);var l=b.getImageData(0,0,1,1).data;l=l[0]<<16^l[1]<<8^l[2];2048<g&&(c=Object.create(null),g=0);c[e]=l;g++;return l}return e&&e.isColor?e.getHex():0}}(),L=Object.freeze({__proto__:null,number:r,color:function(a,b,c){a=K(a);b=K(b);var g=r(a>>16&255,b>>16&255,c),e=r(a>>8&255,b>>8&255,c);a=r(a&255,b&255,c);return g<<16^e<<8^a}}),M=function(a){return a};q=function(a,b,c,g,e,l,h,f,d){void 0===g&&(g=750);void 0===e&&(e=0);void 0=== | ||
l&&(l=M);void 0===h&&(h=1);void 0===f&&(f="forward");void 0===d&&(d="number");this.callback=a;this.fromValue=b;this.toValue=c;this.duration=g;this.delay=e;this.easing="string"===typeof l?J[l]||M:l;this.iterations=h;this.direction=f;this.interpolate="function"===typeof d?d:L[d]||r;this.totalElapsed=9007199254740991>this.iterations?this.delay+this.duration*this.iterations:9007199254740991};q.prototype.gotoElapsedTime=function(a){var b=this.duration,c=this.delay;if(a>=c){a=Math.min(a,this.totalElapsed)- | ||
c;c=a%b/b;0===c&&0!==a&&(c=1);c=this.easing(c);if("reverse"===this.direction||"alternate"===this.direction&&0===Math.ceil(a/b)%2)c=1-c;this.callback(this.interpolate(this.fromValue,this.toValue,c))}};q.prototype.gotoEnd=function(){this.gotoElapsedTime(this.totalElapsed)};A=function(a){function b(b,g,e,d,h,f){"number"!==typeof g&&(g=b.reduce(function(a,b){return Math.max(a,b.totalElapsed)},0));Infinity===g&&(g=Number.MAX_VALUE);a.call(this,null,0,g,g,e,d,h,f);1===b.length?this.callback=b[0].gotoElapsedTime.bind(b[0]): | ||
(b.sort(N),this.callback=this._syncTweens);this.tweens=b}a&&(b.__proto__=a);b.prototype=Object.create(a&&a.prototype);b.prototype.constructor=b;b.prototype._syncTweens=function(a){for(var b=0,c=this.tweens.length;b<c;b++)this.tweens[b].gotoElapsedTime(a)};return b}(q);var p=[],n=null,w=!1,x=window;d=function(){this.tweens=[]};d.prototype.destructor=function(){this.tweens=null;this.runner$running=!1;w=!0;this.start=this.stop=this.pause=this._tick=O};d.prototype.start=function(a){a.runner$paused&&a.runner$started? | ||
a.runner$started+=Date.now()-a.runner$paused:this.tweens.push(a);a.runner$paused=null;a.runner$stopped=!1;this.runner$running||(this.runner$running=!0,p.push(this),y())};d.prototype.stop=function(a){a.runner$stopped=!0;a.runner$paused=null};d.prototype.pause=function(a){a.runner$paused||(a.runner$paused=Date.now())};d.prototype.stopAll=function(){this.tweens&&this.tweens.forEach(this.stop,this)};d.prototype._tick=function(a){for(var b=this.tweens,c=!1,d=!1,e=0,f=b.length;e<f;e++){var h=b[e];if(!h.runner$stopped&& | ||
!h.runner$paused){var k=a-(h.runner$started||(h.runner$started=a));h.gotoElapsedTime(k);d=!0;if(k>h.totalElapsed&&(this.stop(h),h.onDone))h.onDone()}h.runner$stopped&&(c=!0)}if(d)this.onTick();if(c&&(this.tweens=b.filter(Q),!this.tweens.length&&(this.runner$running=!1,w=!0,this.onDone)))this.onDone()};d.prototype.onTick=function(){};d.prototype.onDone=function(){};f.Easings=J;f.Interpolators=L;f.MultiTween=A;f.Runner=d;f.Tween=q;f.setAnimationScheduler=function(a){a=a||window;a!==x&&(n&&(x.cancelAnimationFrame(n), | ||
n=null),x=a,y())};Object.defineProperty(f,"__esModule",{value:!0})}) | ||
'use strict';(function(e,p){"object"===typeof exports&&"undefined"!==typeof module?p(exports):"function"===typeof define&&define.amd?define(["exports"],p):(e=e||self,p(e.troika_animation={}))})(this,function(e){function p(a,b){return function(c){return.5>c?.5*a(2*c):.5*b(2*c-1)+.5}}function u(a){return function(b){return k(b,a)}}function v(a){return function(b){return 1-k(1-b,a)}}function w(a){return function(b){return.5>b?.5*k(2*b,a):.5*(1-k(1-(2*b-1),a))+.5}}function t(a,b,c){return a+(b-a)*c}function R(a, | ||
b){return a.totalElapsed-b.totalElapsed}function S(){}function T(a){return a.runner$running}function U(a){return!a.runner$stopped}function V(){var a=Date.now();q=null;x&&(r=r.filter(T),x=!1);if(r.length){for(var b=r.length;0<b--;)r[b]._tick(a);B()}}function B(){q||(q=y.requestAnimationFrame(V))}var k=Math.pow,C=Math.PI,G=Math.sqrt,H=C/2,W=2*C,z=u(2),D=v(2),l=w(2),n=u(3),X=v(3),Y=w(3),Z=u(4),aa=v(4),ba=w(4),ca=u(5),da=v(5),ea=w(5),I=function(a){return 1-G(1-a*a)},J=function(a){return G(1-k(a-1,2))}, | ||
fa=p(I,J),K=function(a){return 0===a||1===a?a:1-E(1-a)},E=function(a){return 0===a||1===a?a:Math.pow(2,-10*a)*Math.sin((a-.075)*W/.3)+1},ha=p(K,E),L=function(a){return 1-F(1-a)},F=function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},ia=p(L,F),M=Object.freeze({__proto__:null,linear:function(a){return a},easeInQuad:z,easeOutQuad:D,easeInOutQuad:l,easeInCubic:n,easeOutCubic:X,easeInOutCubic:Y,easeInQuart:Z, | ||
easeOutQuart:aa,easeInOutQuart:ba,easeInQuint:ca,easeOutQuint:da,easeInOutQuint:ea,easeInSine:function(a){return 1-Math.cos(a*H)},easeOutSine:function(a){return Math.sin(a*H)},easeInOutSine:function(a){return-.5*(Math.cos(C*a)-1)},easeInExpo:function(a){return 0===a?0:k(2,10*(a-1))},easeOutExpo:function(a){return 1===a?1:1-k(2,-10*a)},easeInOutExpo:function(a){return 0===a||1===a?a:.5>a?.5*k(2,10*(2*a-1)):.5*(1-k(2,-10*(2*a-1)))+.5},easeInCirc:I,easeOutCirc:J,easeInOutCirc:fa,easeInElastic:K,easeOutElastic:E, | ||
easeInOutElastic:ha,easeInBack:function(a){return a*a*(2.70158*a-1.70158)},easeOutBack:function(a){return--a*a*(2.70158*a+1.70158)+1},easeInOutBack:function(a){return 1>(a*=2)?.5*a*a*(3.5949095*a-2.5949095):.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)},easeInBounce:L,easeOutBounce:F,easeInOutBounce:ia}),N=function(){var a,b,c=Object.create(null),f=0;return function(g){if("number"===typeof g)return g;if("string"===typeof g){if(g in c)return c[g];a||(a=document.createElement("canvas"),b=a.getContext("2d")); | ||
a.width=a.height=1;b.fillStyle=g;b.fillRect(0,0,1,1);var d=b.getImageData(0,0,1,1).data;d=d[0]<<16^d[1]<<8^d[2];2048<f&&(c=Object.create(null),f=0);c[g]=d;f++;return d}return g&&g.isColor?g.getHex():0}}(),O=Object.freeze({__proto__:null,number:t,color:function(a,b,c){a=N(a);b=N(b);var f=t(a>>16&255,b>>16&255,c),g=t(a>>8&255,b>>8&255,c);a=t(a&255,b&255,c);return f<<16^g<<8^a}});n=function(){};n.prototype.gotoElapsedTime=function(a){};n.prototype.gotoEnd=function(){};n.prototype.isDoneAtElapsedTime= | ||
function(a){};var P=function(a){return a};z=function(a){function b(b,f,g,d,h,m,e,l,k){void 0===d&&(d=750);void 0===h&&(h=0);void 0===m&&(m=P);void 0===e&&(e=1);void 0===l&&(l="forward");void 0===k&&(k="number");a.call(this);this.callback=b;this.fromValue=f;this.toValue=g;this.duration=d;this.delay=h;this.easing="string"===typeof m?M[m]||P:m;this.iterations=e;this.direction=l;this.interpolate="function"===typeof k?k:O[k]||t;this.totalElapsed=9007199254740991>this.iterations?this.delay+this.duration* | ||
this.iterations:9007199254740991}a&&(b.__proto__=a);b.prototype=Object.create(a&&a.prototype);b.prototype.constructor=b;b.prototype.gotoElapsedTime=function(a){var b=this.duration,c=this.delay;if(a>=c){a=Math.min(a,this.totalElapsed)-c;c=a%b/b;0===c&&0!==a&&(c=1);c=this.easing(c);if("reverse"===this.direction||"alternate"===this.direction&&0===Math.ceil(a/b)%2)c=1-c;this.callback(this.interpolate(this.fromValue,this.toValue,c))}};b.prototype.gotoEnd=function(){this.gotoElapsedTime(this.totalElapsed)}; | ||
b.prototype.isDoneAtElapsedTime=function(a){return a>this.totalElapsed};return b}(n);D=function(a){function b(b,f,g,d,h,m){"number"!==typeof f&&(f=b.reduce(function(a,b){return Math.max(a,b.totalElapsed)},0));Infinity===f&&(f=Number.MAX_VALUE);a.call(this,null,0,f,f,g,d,h,m);1===b.length?this.callback=b[0].gotoElapsedTime.bind(b[0]):(b.sort(R),this.callback=this._syncTweens);this.tweens=b}a&&(b.__proto__=a);b.prototype=Object.create(a&&a.prototype);b.prototype.constructor=b;b.prototype._syncTweens= | ||
function(a){for(var b=0,c=this.tweens.length;b<c;b++)this.tweens[b].gotoElapsedTime(a)};return b}(z);var r=[],q=null,x=!1,y=window;l=function(){this.tweens=[]};l.prototype.destructor=function(){this.tweens=null;this.runner$running=!1;x=!0;this.start=this.stop=this.pause=this._tick=S};l.prototype.start=function(a){a.runner$paused&&a.runner$started?a.runner$started+=Date.now()-a.runner$paused:this.tweens.push(a);a.runner$paused=null;a.runner$stopped=!1;this.runner$running||(this.runner$running=!0,r.push(this), | ||
B())};l.prototype.stop=function(a){a.runner$stopped=!0;a.runner$paused=null};l.prototype.pause=function(a){a.runner$paused||(a.runner$paused=Date.now())};l.prototype.stopAll=function(){this.tweens&&this.tweens.forEach(this.stop,this)};l.prototype._tick=function(a){for(var b=this.tweens,c=!1,f=!1,g=0,d=b.length;g<d;g++){var h=b[g];if(!h.runner$stopped&&!h.runner$paused){var m=a-(h.runner$started||(h.runner$started=a));h.gotoElapsedTime(m);f=!0;if(h.isDoneAtElapsedTime(m)&&(this.stop(h),h.onDone))h.onDone()}h.runner$stopped&& | ||
(c=!0)}if(f)this.onTick();if(c&&(this.tweens=b.filter(U),!this.tweens.length&&(this.runner$running=!1,x=!0,this.onDone)))this.onDone()};l.prototype.onTick=function(){};l.prototype.onDone=function(){};var Q={default:{mass:1,tension:170,friction:26},gentle:{mass:1,tension:120,friction:14},wobbly:{mass:1,tension:180,friction:12},stiff:{mass:1,tension:210,friction:20},slow:{mass:1,tension:280,friction:60},molasses:{mass:1,tension:280,friction:120}},A=Q.default;n=function(a){function b(b,f,g,d,h,m){void 0=== | ||
h&&(h=0);void 0===m&&(m=0);a.call(this);this.isSpring=!0;this.callback=b;this.currentValue=f;this.toValue=g;this.velocity=h;this.delay=m;"string"===typeof d&&(d=Q[d]);d||(d=A);b=d.mass;f=d.tension;d=d.friction;this.mass="number"===typeof b?b:A.mass;this.tension=1E-6*("number"===typeof f?f:A.tension);this.friction=.001*("number"===typeof d?d:A.friction);this.minAcceleration=1E-10;this.$lastTime=m;this.$endTime=Infinity}a&&(b.__proto__=a);b.prototype=Object.create(a&&a.prototype);b.prototype.constructor= | ||
b;b.prototype.gotoElapsedTime=function(a){if(a>=this.delay){for(var b=this.toValue,c=this.mass,d=this.tension,h=this.friction,m=this.minAcceleration,e=this.velocity||0,k=this.currentValue,l=this.$lastTime;l<a;l++){var n=(d*(b-k)-h*e)/c;if(Math.abs(n)<m){e=0;k=b;this.$endTime=l;break}else e+=n,k+=e}this.velocity=e;this.$lastTime=a;this.callback(this.currentValue=k)}};b.prototype.gotoEnd=function(){this.velocity=0;this.$lastTime=this.$endTime;this.callback(this.currentValue=this.toValue)};b.prototype.isDoneAtElapsedTime= | ||
function(a){return a>=this.$endTime};return b}(n);e.Easings=M;e.Interpolators=O;e.MultiTween=D;e.Runner=l;e.SpringTween=n;e.Tween=z;e.setAnimationScheduler=function(a){a=a||window;a!==y&&(q&&(y.cancelAnimationFrame(q),q=null),y=a,B())};Object.defineProperty(e,"__esModule",{value:!0})}) |
{ | ||
"name": "troika-animation", | ||
"version": "0.34.0", | ||
"version": "0.35.0", | ||
"description": "Troika Animation Utilities", | ||
@@ -16,3 +16,3 @@ "author": "Jason Johnston <jason.johnston@protectwise.com>", | ||
"module:src": "src/index.js", | ||
"gitHead": "b19cd3aff4b0876253d76b3fc66b7e2a1f16a7e5" | ||
"gitHead": "0fc459bec5f330f6f040d07bfe304d10a87d73c0" | ||
} |
@@ -64,2 +64,26 @@ # Troika Animation | ||
### SpringTween | ||
> See the JSDoc comments in [SpringTween.js](./src/SpringTween.js) for more details about each parameter. | ||
This is a lot like `Tween` but instead of having a fixed duration, its value is transitioned using a simple spring physics simulation. | ||
```js | ||
import { SpringTween } from 'troika-animation' | ||
const tween = new Tween( | ||
onTweenFrame, // callback | ||
-100, // fromValue | ||
100, // toValue | ||
{ // spring simulation config, or the name of a preset | ||
mass: 1, // object mass | ||
tension: 170, // spring tension force | ||
friction: 26 // friction force | ||
}, | ||
0 // delay | ||
) | ||
``` | ||
The meanings of the spring configuration parameters match those from [react-spring](https://www.react-spring.io/docs/hooks/api). The [named presets](./src/SpringPresets.js) also match those defined by react-spring. | ||
### Runner | ||
@@ -66,0 +90,0 @@ |
@@ -0,0 +0,0 @@ /* |
@@ -12,1 +12,2 @@ // Animation | ||
export {default as Tween} from './Tween.js' | ||
export {default as SpringTween} from './SpringTween.js' |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ import Tween from './Tween.js' |
@@ -151,3 +151,3 @@ let runners = [] | ||
// Queue for removal if we're past its end time | ||
if (elapsed > tween.totalElapsed) { | ||
if (tween.isDoneAtElapsedTime(elapsed)) { | ||
this.stop(tween) | ||
@@ -154,0 +154,0 @@ if (tween.onDone) { |
import * as Easings from './Easings.js' | ||
import * as Interpolators from './Interpolators.js' | ||
import AbstractTween from './AbstractTween.js' | ||
@@ -31,4 +32,5 @@ const linear = v => v | ||
*/ | ||
class Tween { | ||
class Tween extends AbstractTween { | ||
constructor(callback, fromValue, toValue, duration=750, delay=0, easing=linear, iterations=1, direction='forward', interpolate='number') { | ||
super() | ||
this.callback = callback | ||
@@ -81,4 +83,13 @@ this.fromValue = fromValue | ||
} | ||
/** | ||
* For a given elapsed time relative to the start of the tween, determines if the tween is in its completed end state. | ||
* @param {number} time | ||
* @return {boolean} | ||
*/ | ||
isDoneAtElapsedTime(time) { | ||
return time > this.totalElapsed | ||
} | ||
} | ||
export default Tween |
Sorry, the diff of this file is not supported yet
121406
20
2649
126