module import { EventEmitter } from "events" ; import { ANIMATE_DEFAULTS , AnimateOptions } from "../utils/animate" ; import easings from "../utils/easings" ; import { cancelFrame , requestFrame } from "../utils" ; import { CameraState } from "../types" ; /**
 * Defaults.
 */ const DEFAULT_ZOOMING_RATIO = 1.5 ; constructor export default class Camera extends EventEmitter implements CameraState { x = 0.5 ; y = 0.5 ; angle = 0 ; ratio = 1 ; minRatio : number | null = null ; maxRatio : number | null = null ; private nextFrame : number | null = null ; private previousState : CameraState | null = null ; private enabled = true ; animationCallback ? : ( ) => void ; constructor ( ) { super ( ) ; this . previousState = this . getState ( ) ; } param state return { Camera } static from ( state ) { const camera = new Camera ( ) ; return camera . setState ( state ) ; } return { Camera } enable ( ) { this . enabled = true ; return this ; } return { Camera } disable ( ) { this . enabled = false ; return this ; } return { object } getState ( ) { return { x : this . x , y : this . y , angle : this . angle , ratio : this . ratio , } ; } return { object } hasState ( state ) { return this . x === state . x && this . y === state . y && this . ratio === state . ratio && this . angle === state . angle ; } return { object } getPreviousState ( ) { const state = this . previousState ; if ( ! state ) return null ; return { x : state . x , y : state . y , angle : state . angle , ratio : state . ratio , } ; } param ratio return { number } getBoundedRatio ( ratio ) { let r = ratio ; if ( typeof this . minRatio === "number" ) r = Math . max ( r , this . minRatio ) ; if ( typeof this . maxRatio === "number" ) r = Math . min ( r , this . maxRatio ) ; return r ; } param state return { object } validateState ( state ) { const validatedState = { } ; if ( typeof state . x === "number" ) validatedState . x = state . x ; if ( typeof state . y === "number" ) validatedState . y = state . y ; if ( typeof state . angle === "number" ) validatedState . angle = state . angle ; if ( typeof state . ratio === "number" ) validatedState . ratio = this . getBoundedRatio ( state . ratio ) ; return validatedState ; } return { boolean } isAnimated ( ) { return ! ! this . nextFrame ; } param { object } state return { Camera } setState ( state ) { if ( ! this . enabled ) return this ; this . previousState = this . getState ( ) ; const validState = this . validateState ( state ) ; if ( typeof validState . x === "number" ) this . x = validState . x ; if ( typeof validState . y === "number" ) this . y = validState . y ; if ( typeof validState . angle === "number" ) this . angle = validState . angle ; if ( typeof validState . ratio === "number" ) this . ratio = validState . ratio ; if ( ! this . hasState ( this . previousState ) ) this . emit ( "updated" , this . getState ( ) ) ; return this ; } param { object } state param { object } opts param { number } duration param { string | number param { function } callback animate ( state , opts ? , callback ? ) { if ( ! this . enabled ) return ; const options = Object . assign ( { } , ANIMATE_DEFAULTS , opts ) ; const validState = this . validateState ( state ) ; const easing = typeof options . easing === "function" ? options . easing : easings [ options . easing ] ; const start = Date . now ( ) , initialState = this . getState ( ) ; const fn = ( ) => { const t = ( Date . now ( ) - start ) / options . duration ; if ( t >= 1 ) { this . nextFrame = null ; this . setState ( validState ) ; if ( this . animationCallback ) { this . animationCallback . call ( null ) ; this . animationCallback = undefined ; } return ; } const coefficient = easing ( t ) ; const newState = { } ; if ( validState . x ) newState . x = initialState . x + ( validState . x - initialState . x ) * coefficient ; if ( validState . y ) newState . y = initialState . y + ( validState . y - initialState . y ) * coefficient ; if ( validState . angle ) newState . angle = initialState . angle + ( validState . angle - initialState . angle ) * coefficient ; if ( validState . ratio ) newState . ratio = initialState . ratio + ( validState . ratio - initialState . ratio ) * coefficient ; this . setState ( newState ) ; this . nextFrame = requestFrame ( fn ) ; } ; if ( this . nextFrame ) { cancelFrame ( this . nextFrame ) ; if ( this . animationCallback ) this . animationCallback . call ( null ) ; this . nextFrame = requestFrame ( fn ) ; } else { fn ( ) ; } this . animationCallback = callback ; } param { number | object } factorOrOptions return { function } animatedZoom ( factorOrOptions ? ) { if ( ! factorOrOptions ) { this . animate ( { ratio : this . ratio / DEFAULT_ZOOMING_RATIO } ) ; } else { if ( typeof factorOrOptions === "number" ) return this . animate ( { ratio : this . ratio / factorOrOptions } ) ; else this . animate ( { ratio : this . ratio / ( factorOrOptions . factor || DEFAULT_ZOOMING_RATIO ) , } , factorOrOptions ) ; } } param { number | object } factorOrOptions animatedUnzoom ( factorOrOptions ? ) { if ( ! factorOrOptions ) { this . animate ( { ratio : this . ratio * DEFAULT_ZOOMING_RATIO } ) ; } else { if ( typeof factorOrOptions === "number" ) return this . animate ( { ratio : this . ratio * factorOrOptions } ) ; else this . animate ( { ratio : this . ratio * ( factorOrOptions . factor || DEFAULT_ZOOMING_RATIO ) , } , factorOrOptions ) ; } } param { object } options animatedReset ( options ? ) { this . animate ( { x : 0.5 , y : 0.5 , ratio : 1 , angle : 0 , } , options ) ; } return { Camera } copy ( ) { return Camera . from ( this . getState ( ) ) ; } }