import Interpolator from './Interpolator' ; import { fireOnce } from '../utils' ; export default class SpringInterpolator extends Interpolator { private stiffness : number ; private damping : number ; private precision : number ; constructor ( stiffness = 170 , damping = 26 , precision = 0.01 ) { super ( ) ; this . stiffness = stiffness ; this . damping = damping ; this . precision = precision ; } public play ( element , invertObject , reverse = false , callback ? ) { element . style . transformOrigin = '0 0' ; element . style . transition = 'transform 300ms ease-in-out' ; element . style . transform = `translate(${ invertObject . x }px, ${ invertObject . y }px) scale(${ invertObject . sx }, ${ invertObject . sy })` ; } public playMultiple ( elements , invertObject , reverse = false , callback ? ) { const map = new Map ( ) ; elements . forEach ( ( el , idx ) => { el . style . transformOrigin = '0 0' ; const invert = invertObject [ idx ] ; if ( reverse ) { map . set ( el , { invert : { sx : 1 , sy : 1 , x : 0 , y : 0 , } , state : { sx : { isAnimating : ! ( invert . sx === 1 ) , v : 0 , x : invert . sx , } , sy : { isAnimating : ! ( invert . sy === 1 ) , v : 0 , x : invert . sy , } , x : { isAnimating : ! ( invert . x === 0 ) , v : 0 , x : invert . x } , y : { isAnimating : ! ( invert . y === 0 ) , v : 0 , x : invert . y } , } , } ) ; } else { map . set ( el , { invert , state : { sx : { isAnimating : ! ( invert . sx === 1 ) , v : 0 , x : 1 , } , sy : { isAnimating : ! ( invert . sy === 1 ) , v : 0 , x : 1 , } , x : { x : 0 , v : 0 , isAnimating : ! ( invert . x === 0 ) } , y : { x : 0 , v : 0 , isAnimating : ! ( invert . y === 0 ) } , } , } ) ; } } ) ; let animComplete = false ; const update = ( ) => { if ( animComplete ) { if ( callback ) { callback ( ) ; } return ; } requestAnimationFrame ( update ) ; elements . forEach ( ( element , idx ) => { const obj = map . get ( element ) ; if ( obj ) { let currState = obj . state ; const invert = obj . invert ; const updatedX = this . getMotion ( currState . x . x , currState . x . v , invert . x ) ; const updatedY = this . getMotion ( currState . y . x , currState . y . v , invert . y ) ; const updatedSx = this . getMotion ( currState . sx . x , currState . sx . v , invert . sx ) ; const updatedSy = this . getMotion ( currState . sy . x , currState . sy . v , invert . sy ) ; element . style . transform = `translate(${ updatedX . x }px, ${ updatedY . x }px) scale(${ updatedSx . x }, ${ updatedSy . x })` ; currState = { sx : { ... updatedSx , isAnimating : ! ( updatedSx . x === invert . sx ) , } , sy : { ... updatedSy , isAnimating : ! ( updatedSy . x === invert . sy ) , } , x : { ... updatedX , isAnimating : ! ( updatedX . x === invert . x ) , } , y : { ... updatedY , isAnimating : ! ( updatedY . x === invert . y ) , } , } ; if ( currState . sx . isAnimating || currState . sy . isAnimating || currState . x . isAnimating || currState . y . isAnimating ) { map . set ( element , { invert , state : currState , } ) ; } else { element . style . transform = '' ; element . style . transformOrigin = '' ; map . delete ( element ) ; } } } ) ; animComplete = map . size === 0 ; } ; if ( reverse ) { update ( ) ; } else { requestAnimationFrame ( ( ) => { requestAnimationFrame ( ( ) => { update ( ) ; } ) ; } ) ; } } private getMotion = ( currX , currV , finalX ) : { x : number ; v : number ; } => { if ( currX === finalX ) { return { x : finalX , v : 0 } ; } const seconds = 0.0166 ; const Fspring = - this . stiffness * ( currX - finalX ) ; const Fdamping = - this . damping * currV ; const a = Fspring + Fdamping ; const newV = currV + a * seconds ; const newX = currX + newV * seconds ; const precision = this . precision ; if ( Math . abs ( newV ) < precision && Math . abs ( newX - finalX ) < precision ) { return { x : finalX , v : 0 } ; } return { x : newX , v : newV } ; } ; }