diff options
Diffstat (limited to 'src/animation/Controller.js')
-rw-r--r-- | src/animation/Controller.js | 252 |
1 files changed, 174 insertions, 78 deletions
diff --git a/src/animation/Controller.js b/src/animation/Controller.js index cee7115..9efe87e 100644 --- a/src/animation/Controller.js +++ b/src/animation/Controller.js @@ -7,85 +7,149 @@ Base Class The base stepper class that will be ***/ -function makeSetterGetter (k, f) { - return function (v) { - if (v == null) return this[v] +function makeSetterGetter ( k, f ) { + + return function ( v ) { + + if ( v == null ) return this[v] this[k] = v - if (f) f.call(this) + if ( f ) f.call( this ) return this + } + } export let easing = { - '-': function (pos) { return pos }, - '<>': function (pos) { return -Math.cos(pos * Math.PI) / 2 + 0.5 }, - '>': function (pos) { return Math.sin(pos * Math.PI / 2) }, - '<': function (pos) { return -Math.cos(pos * Math.PI / 2) + 1 }, - bezier: function (x1, y1, x2, y2) { + '-': function ( pos ) { + + return pos + + }, + '<>': function ( pos ) { + + return -Math.cos( pos * Math.PI ) / 2 + 0.5 + + }, + '>': function ( pos ) { + + return Math.sin( pos * Math.PI / 2 ) + + }, + '<': function ( pos ) { + + return -Math.cos( pos * Math.PI / 2 ) + 1 + + }, + bezier: function ( x1, y1, x2, y2 ) { + // see https://www.w3.org/TR/css-easing-1/#cubic-bezier-algo - return function (t) { - if (t < 0) { - if (x1 > 0) { + return function ( t ) { + + if ( t < 0 ) { + + if ( x1 > 0 ) { + return y1 / x1 * t - } else if (x2 > 0) { + + } else if ( x2 > 0 ) { + return y2 / x2 * t + } else { + return 0 + } - } else if (t > 1) { - if (x2 < 1) { - return (1 - y2) / (1 - x2) * t + (y2 - x2) / (1 - x2) - } else if (x1 < 1) { - return (1 - y1) / (1 - x1) * t + (y1 - x1) / (1 - x1) + + } else if ( t > 1 ) { + + if ( x2 < 1 ) { + + return ( 1 - y2 ) / ( 1 - x2 ) * t + ( y2 - x2 ) / ( 1 - x2 ) + + } else if ( x1 < 1 ) { + + return ( 1 - y1 ) / ( 1 - x1 ) * t + ( y1 - x1 ) / ( 1 - x1 ) + } else { + return 1 + } + } else { - return 3 * t * (1 - t) ** 2 * y1 + 3 * t ** 2 * (1 - t) * y2 + t ** 3 + + return 3 * t * ( 1 - t ) ** 2 * y1 + 3 * t ** 2 * ( 1 - t ) * y2 + t ** 3 + } + } + }, // see https://www.w3.org/TR/css-easing-1/#step-timing-function-algo - steps: function (steps, stepPosition = 'end') { + steps: function ( steps, stepPosition = 'end' ) { + // deal with "jump-" prefix - stepPosition = stepPosition.split('-').reverse()[0] + stepPosition = stepPosition.split( '-' ).reverse()[0] let jumps = steps - if (stepPosition === 'none') { + if ( stepPosition === 'none' ) { + --jumps - } else if (stepPosition === 'both') { + + } else if ( stepPosition === 'both' ) { + ++jumps + } // The beforeFlag is essentially useless - return (t, beforeFlag = false) => { + return ( t, beforeFlag = false ) => { + // Step is called currentStep in referenced url - let step = Math.floor(t * steps) - const jumping = (t * step) % 1 === 0 + let step = Math.floor( t * steps ) + const jumping = ( t * step ) % 1 === 0 + + if ( stepPosition === 'start' || stepPosition === 'both' ) { - if (stepPosition === 'start' || stepPosition === 'both') { ++step + } - if (beforeFlag && jumping) { + if ( beforeFlag && jumping ) { + --step + } - if (t >= 0 && step < 0) { + if ( t >= 0 && step < 0 ) { + step = 0 + } - if (t <= 1 && step > jumps) { + if ( t <= 1 && step > jumps ) { + step = jumps + } return step / jumps + } + } } export class Stepper { - done () { return false } + + done () { + + return false + + } + } /*** @@ -94,17 +158,25 @@ Easing Functions ***/ export class Ease extends Stepper { - constructor (fn) { + + constructor ( fn ) { + super() this.ease = easing[fn || timeline.ease] || fn + } - step (from, to, pos) { - if (typeof from !== 'number') { + step ( from, to, pos ) { + + if ( typeof from !== 'number' ) { + return pos < 1 ? from : to + } - return from + (to - from) * this.ease(pos) + return from + ( to - from ) * this.ease( pos ) + } + } /*** @@ -113,51 +185,65 @@ Controller Types ***/ export class Controller extends Stepper { - constructor (fn) { + + constructor ( fn ) { + super() this.stepper = fn + } - step (current, target, dt, c) { - return this.stepper(current, target, dt, c) + step ( current, target, dt, c ) { + + return this.stepper( current, target, dt, c ) + } - done (c) { + done ( c ) { + return c.done + } + } function recalculate () { + // Apply the default parameters - var duration = (this._duration || 500) / 1000 + var duration = ( this._duration || 500 ) / 1000 var overshoot = this._overshoot || 0 // Calculate the PID natural response var eps = 1e-10 var pi = Math.PI - var os = Math.log(overshoot / 100 + eps) - var zeta = -os / Math.sqrt(pi * pi + os * os) - var wn = 3.9 / (zeta * duration) + var os = Math.log( overshoot / 100 + eps ) + var zeta = -os / Math.sqrt( pi * pi + os * os ) + var wn = 3.9 / ( zeta * duration ) // Calculate the Spring values this.d = 2 * zeta * wn this.k = wn * wn + } export class Spring extends Controller { - constructor (duration, overshoot) { + + constructor ( duration, overshoot ) { + super() - this.duration(duration || 500) - .overshoot(overshoot || 0) + this.duration( duration || 500 ) + .overshoot( overshoot || 0 ) + } - step (current, target, dt, c) { - if (typeof current === 'string') return current + step ( current, target, dt, c ) { + + if ( typeof current === 'string' ) return current c.done = dt === Infinity - if (dt === Infinity) return target - if (dt === 0) return current + if ( dt === Infinity ) return target + if ( dt === 0 ) return current - if (dt > 100) dt = 16 + if ( dt > 100 ) dt = 16 dt /= 1000 @@ -165,65 +251,75 @@ export class Spring extends Controller { var velocity = c.velocity || 0 // Apply the control to get the new position and store it - var acceleration = -this.d * velocity - this.k * (current - target) - var newPosition = current + - velocity * dt + - acceleration * dt * dt / 2 + var acceleration = -this.d * velocity - this.k * ( current - target ) + var newPosition = current + + velocity * dt + + acceleration * dt * dt / 2 // Store the velocity c.velocity = velocity + acceleration * dt // Figure out if we have converged, and if so, pass the value - c.done = Math.abs(target - newPosition) + Math.abs(velocity) < 0.002 + c.done = Math.abs( target - newPosition ) + Math.abs( velocity ) < 0.002 return c.done ? target : newPosition + } + } -extend(Spring, { - duration: makeSetterGetter('_duration', recalculate), - overshoot: makeSetterGetter('_overshoot', recalculate) -}) +extend( Spring, { + duration: makeSetterGetter( '_duration', recalculate ), + overshoot: makeSetterGetter( '_overshoot', recalculate ) +} ) export class PID extends Controller { - constructor (p, i, d, windup) { + + constructor ( p, i, d, windup ) { + super() p = p == null ? 0.1 : p i = i == null ? 0.01 : i d = d == null ? 0 : d windup = windup == null ? 1000 : windup - this.p(p).i(i).d(d).windup(windup) + this.p( p ).i( i ).d( d ).windup( windup ) + } - step (current, target, dt, c) { - if (typeof current === 'string') return current + step ( current, target, dt, c ) { + + if ( typeof current === 'string' ) return current c.done = dt === Infinity - if (dt === Infinity) return target - if (dt === 0) return current + if ( dt === Infinity ) return target + if ( dt === 0 ) return current var p = target - current - var i = (c.integral || 0) + p * dt - var d = (p - (c.error || 0)) / dt + var i = ( c.integral || 0 ) + p * dt + var d = ( p - ( c.error || 0 ) ) / dt var windup = this.windup // antiwindup - if (windup !== false) { - i = Math.max(-windup, Math.min(i, windup)) + if ( windup !== false ) { + + i = Math.max( -windup, Math.min( i, windup ) ) + } c.error = p c.integral = i - c.done = Math.abs(p) < 0.001 + c.done = Math.abs( p ) < 0.001 + + return c.done ? target : current + ( this.P * p + this.I * i + this.D * d ) - return c.done ? target : current + (this.P * p + this.I * i + this.D * d) } + } -extend(PID, { - windup: makeSetterGetter('windup'), - p: makeSetterGetter('P'), - i: makeSetterGetter('I'), - d: makeSetterGetter('D') -}) +extend( PID, { + windup: makeSetterGetter( 'windup' ), + p: makeSetterGetter( 'P' ), + i: makeSetterGetter( 'I' ), + d: makeSetterGetter( 'D' ) +} ) |