import bezier from 'bezier-easing' ; export abstract class AnimatedNode { abstract getValue ( observer ? ) ; interpolate ( inRange , outRange , ease = Easing . linear ) { return new AnimatedInterpolate ( this , inRange , outRange , ease ) ; } } export class AnimatedValue extends AnimatedNode { private _observer ? : Function ; constructor ( private _value ) { super ( ) ; } setValue ( value ) { this . _value = value ; if ( this . _observer ) { const observer = this . _observer ; this . _observer = void 0 ; observer ( ) ; } } getValue ( observer ? ) { if ( observer ) { this . _observer = observer ; } return this . _value ; } } export class AnimatedInterpolate extends AnimatedNode { constructor ( private source , private inRange , private outRange , private ease ) { super ( ) ; } getValue ( observer ? ) { const value = this . source . getValue ( observer ) ; const len = this . inRange . length ; let i = 1 ; for ( ; i < len ; i ++ ) { const x1 = this . inRange [ i ] ; if ( value < x1 || i === len - 1 ) { const x0 = this . inRange [ i - 1 ] ; const y0 = this . outRange [ i - 1 ] ; const y1 = this . outRange [ i ] ; const percent = ( value - x0 ) / ( x1 - x0 ) ; const result = ( y1 - y0 ) * this . ease ( percent ) + y0 ; return result ; } } return 0 ; } } export type TimingConfig = { to : number ; duration : number ; ease ? : ( t : number ) => number ; } ; export class AnimatedTiming { private _startTime = 0 ; private _startValue = 0 ; private _onEnd ? : Function ; private _next = 0 ; constructor ( private value , private config ) { } private get _ease ( ) { return this . config . ease || Easing . linear ; } start ( onEnd ? ) { this . _startTime = Date . now ( ) ; this . _startValue = this . value . getValue ( ) ; this . _onEnd = onEnd ; this . _next = requestAnimationFrame ( this . _loop ) ; return this ; } stop ( ) { cancelAnimationFrame ( this . _next ) ; return this ; } promise ( ) { return new Promise ( resolve => ( this . _onEnd = resolve ) ) ; } private _loop = ( ) => { const duration = Date . now ( ) - this . _startTime ; if ( duration < this . config . duration ) { const percent = duration / this . config . duration ; const inc = this . _ease ( percent ) * ( this . config . to - this . _startValue ) ; this . value . setValue ( this . _startValue + inc ) ; this . _next = requestAnimationFrame ( this . _loop ) ; } else { this . value . setValue ( this . config . to ) ; this . _onEnd && requestAnimationFrame ( this . _onEnd as any ) ; } } ; } export function timing ( value , config ) { return new AnimatedTiming ( value , config ) ; } let ease ; function getEase ( ) { if ( ! ease ) { ease = bezier ( 0.42 , 0 , 1 , 1 ) ; } return ease ; } export const Easing = { linear ( t ) { return t ; } , ease ( t ) { return getEase ( ) ( t ) ; } , /**
     * Runs an easing function forwards.
     */ in ( easing = getEase ( ) ) { return easing ; } , /**
     * Runs an easing function backwards.
     */ out ( easing = getEase ( ) ) { return t => 1 - easing ( 1 - t ) ; } , /**
     * Makes any easing function symmetrical. The easing function will run
     * forwards for half of the duration, then backwards for the rest of the
     * duration.
     */ inOut ( easing = getEase ( ) ) { return t => { if ( t < 0.5 ) { return easing ( t * 2 ) / 2 ; } return 1 - easing ( ( 1 - t ) * 2 ) / 2 ; } ; } , bounce ( t ) { if ( t < 1 / 2.75 ) { return 7.5625 * t * t ; } if ( t < 2 / 2.75 ) { const t2 = t - 1.5 / 2.75 ; return 7.5625 * t2 * t2 + 0.75 ; } if ( t < 2.5 / 2.75 ) { const t2 = t - 2.25 / 2.75 ; return 7.5625 * t2 * t2 + 0.9375 ; } const t2 = t - 2.625 / 2.75 ; return 7.5625 * t2 * t2 + 0.984375 ; } , elastic ( bounciness = 1 ) { const p = bounciness * Math . PI ; return t => 1 - Math . pow ( Math . cos ( ( t * Math . PI ) / 2 ) , 3 ) * Math . cos ( t * p ) ; } , } ;