license import { Animated , Easing , EasingFunction , Platform , ViewStyle , } from 'react-native' ; import { Animation , AnimationConfig , } from '../animation/animation' ; const PI = 180 ; const PI2 = 360 ; const OFFSET_MIN = PI / 12 ; const OFFSET_MAX = PI / 6 ; const BaseBezierEasing = Easing . bezier ( 0.4 , 0.0 , 0.7 , 1.0 ) ; const StartArcEasing = ( progress ) : number => { return - PI + OFFSET_MIN + ( PI - OFFSET_MAX ) * BaseBezierEasing ( progress ) ; } ; const EndArcEasing = ( progress ) : number => { return PI2 - OFFSET_MIN + ( - PI + OFFSET_MAX ) * BaseBezierEasing ( progress ) ; } ; const DEFAULT_CONFIG = { duration : 2400 , easing : Easing . linear , cycles : - 1 , useNativeDriver : Platform . OS !== 'web' , } ; type TimingAnimationConfig = Omit < Animated . TimingAnimationConfig , 'toValue' > ; export interface SpinnerAnimationStyle { container : ViewStyle ; start : ViewStyle ; end : ViewStyle ; } export type SpinnerAnimationConfig = AnimationConfig & TimingAnimationConfig ; /**
 * Animates a Spinner in a Material Design way.
 *
 * Thanks these guys for open sourcing the algorithm: https://github.com/n4kz/react-native-indicators
 */ export class SpinnerAnimation extends Animation < SpinnerAnimationConfig , SpinnerAnimationStyle > { private animationValue : Animated . Value ; private animationFrames : number [ ] ; private arcSize : number ; constructor ( arcSize , config ? ) { super ( { ... DEFAULT_CONFIG , ... config } ) ; this . arcSize = arcSize ; this . animationValue = new Animated . Value ( 0 ) ; this . animationFrames = this . createFrameRange ( this . config . duration ) ; } protected get animation ( ) : Animated . CompositeAnimation { return Animated . timing ( this . animationValue , { toValue : 1.0 , ... this . config } ) ; } public start ( callback ? ) { this . animationValue . setValue ( 0 ) ; super . start ( callback ) ; } public stop ( ) { super . stop ( ) ; this . animationValue . setValue ( 0 ) ; } returns { SpinnerAnimationStyle } public toProps ( ) { const containerInterpolation = this . createContainerInterpolation ( ) ; const startArcInterpolation = this . createArcInterpolation ( StartArcEasing ) ; const endArcInterpolation = this . createArcInterpolation ( EndArcEasing ) ; return { container : this . toStyleTransformProp ( containerInterpolation ) , start : this . toStyleTransformProp ( startArcInterpolation ) , end : this . toStyleTransformProp ( endArcInterpolation , { transform : [ { translateY : - this . arcSize / 2 } ] , } ) , } ; } param { number } duration returns private createFrameRange = ( duration ) : number [ ] => { const numberOfFrames = 60 * duration / 1000 ; return new Array ( numberOfFrames ) . fill ( 0 ) ; } ; private createContainerInterpolation = ( ) : Animated . AnimatedInterpolation => { return this . animationValue . interpolate ( { inputRange : [ 0 , 1 ] , outputRange : [ this . toDegValue ( OFFSET_MAX + OFFSET_MIN ) , this . toDegValue ( ( 2 * PI2 + OFFSET_MAX + OFFSET_MIN ) ) , ] , } ) ; } ; private createArcInterpolation = ( easing ) : Animated . AnimatedInterpolation => { return this . animationValue . interpolate ( { inputRange : this . createArcInterpolationInputRange ( ) , outputRange : this . createArcInterpolationOutputRange ( easing ) , } ) ; } ; returns private createArcInterpolationInputRange = ( ) : number [ ] => { return this . animationFrames . map ( ( item , frame ) : number => { return frame / ( this . animationFrames . length - 1 ) ; } ) ; } ; param { ( progress : number ) => number } easing returns private createArcInterpolationOutputRange = ( easing ) : string [ ] => { return this . animationFrames . map ( ( item , frame ) : string => { const progress = 2 * frame / ( this . animationFrames . length - 1 ) ; const boundedProgress = Math . min ( 2.0 - progress , progress ) ; return this . toDegValue ( easing ( boundedProgress ) ) ; } ) ; } ; param { Animated . AnimatedInterpolation } rotate param { ViewStyle } source returns private toStyleTransformProp = ( rotate , source = { } ) : ViewStyle => { const transform = [ ... ( source . transform || [ ] ) , { rotate } ] ; return { ... source , transform } ; } ; param { number } source returns private toDegValue = ( source ) : string => { return `${ source }deg` ; } ; }