| 
				
			 | 
			
			
				@@ -0,0 +1,174 @@ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1
			 | 
			
			
				+/** 
			 | 
		
	
		
			
			| 
				
			 | 
			
				2
			 | 
			
			
				+ * @author wkh237 
			 | 
		
	
		
			
			| 
				
			 | 
			
				3
			 | 
			
			
				+ * @version 0.1.1 
			 | 
		
	
		
			
			| 
				
			 | 
			
				4
			 | 
			
			
				+ */ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				5
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				6
			 | 
			
			
				+// @flow 
			 | 
		
	
		
			
			| 
				
			 | 
			
				7
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				8
			 | 
			
			
				+import React, { Component } from 'react'; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				9
			 | 
			
			
				+import { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				10
			 | 
			
			
				+  Text, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				11
			 | 
			
			
				+  View 
			 | 
		
	
		
			
			| 
				
			 | 
			
				12
			 | 
			
			
				+} from 'react-native'; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				13
			 | 
			
			
				+import Timer from 'react-timer-mixin'; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				14
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				15
			 | 
			
			
				+const HALF_RAD = Math.PI/2 
			 | 
		
	
		
			
			| 
				
			 | 
			
				16
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				17
			 | 
			
			
				+export default class AnimateNumber extends Component { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				18
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				19
			 | 
			
			
				+  props : { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				20
			 | 
			
			
				+    countBy? : ?number, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				21
			 | 
			
			
				+    interval? : ?number, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				22
			 | 
			
			
				+    steps? : ?number, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				23
			 | 
			
			
				+    value : number, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				24
			 | 
			
			
				+    timing : 'linear' | 'easeOut' | 'easeIn' | () => number, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				25
			 | 
			
			
				+    formatter : () => {}, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				26
			 | 
			
			
				+    onProgress : () => {}, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				27
			 | 
			
			
				+    onFinish : () => {} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				28
			 | 
			
			
				+  }; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				29
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				30
			 | 
			
			
				+  static defaultProps = { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				31
			 | 
			
			
				+    interval : 14, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				32
			 | 
			
			
				+    timing : 'linear', 
			 | 
		
	
		
			
			| 
				
			 | 
			
				33
			 | 
			
			
				+    steps : 45, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				34
			 | 
			
			
				+    value : 0, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				35
			 | 
			
			
				+    formatter : (val) => val, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				36
			 | 
			
			
				+    onFinish : () => {} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				37
			 | 
			
			
				+  }; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				38
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				39
			 | 
			
			
				+  static TimingFunctions = { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				40
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				41
			 | 
			
			
				+    linear : (interval:number, progress:number):number => { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				42
			 | 
			
			
				+      return interval 
			 | 
		
	
		
			
			| 
				
			 | 
			
				43
			 | 
			
			
				+    }, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				44
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				45
			 | 
			
			
				+    easeOut : (interval:number, progress:number):number => { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				46
			 | 
			
			
				+      return interval * Math.sin(HALF_RAD*progress) * 5 
			 | 
		
	
		
			
			| 
				
			 | 
			
				47
			 | 
			
			
				+    }, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				48
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				49
			 | 
			
			
				+    easeIn : (interval:number, progress:number):number => { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				50
			 | 
			
			
				+      return interval * Math.sin((HALF_RAD - HALF_RAD*progress)) * 5 
			 | 
		
	
		
			
			| 
				
			 | 
			
				51
			 | 
			
			
				+    }, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				52
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				53
			 | 
			
			
				+  }; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				54
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				55
			 | 
			
			
				+  state : { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				56
			 | 
			
			
				+    value? : ?number, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				57
			 | 
			
			
				+    displayValue? : ?number 
			 | 
		
	
		
			
			| 
				
			 | 
			
				58
			 | 
			
			
				+  }; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				59
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				60
			 | 
			
			
				+  /** 
			 | 
		
	
		
			
			| 
				
			 | 
			
				61
			 | 
			
			
				+   * Animation direction, true means positive, false means negative. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				62
			 | 
			
			
				+   * @type {bool} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				63
			 | 
			
			
				+   */ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				64
			 | 
			
			
				+  direction : bool; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				65
			 | 
			
			
				+  /** 
			 | 
		
	
		
			
			| 
				
			 | 
			
				66
			 | 
			
			
				+   * Start value of last animation. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				67
			 | 
			
			
				+   * @type {number} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				68
			 | 
			
			
				+   */ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				69
			 | 
			
			
				+  startFrom : number; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				70
			 | 
			
			
				+  /** 
			 | 
		
	
		
			
			| 
				
			 | 
			
				71
			 | 
			
			
				+  * End value of last animation. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				72
			 | 
			
			
				+  * @type {number} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				73
			 | 
			
			
				+   */ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				74
			 | 
			
			
				+  endWith : number; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				75
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				76
			 | 
			
			
				+  constructor(props:any) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				77
			 | 
			
			
				+    super(props); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				78
			 | 
			
			
				+    // default values of state and non-state variables 
			 | 
		
	
		
			
			| 
				
			 | 
			
				79
			 | 
			
			
				+    this.state = { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				80
			 | 
			
			
				+      value : 0, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				81
			 | 
			
			
				+      displayValue : 0 
			 | 
		
	
		
			
			| 
				
			 | 
			
				82
			 | 
			
			
				+    } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				83
			 | 
			
			
				+    this.dirty = false; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				84
			 | 
			
			
				+    this.startFrom = 0; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				85
			 | 
			
			
				+    this.endWith = 0; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				86
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				87
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				88
			 | 
			
			
				+  componentDidMount() { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				89
			 | 
			
			
				+    this.startFrom = this.state.value 
			 | 
		
	
		
			
			| 
				
			 | 
			
				90
			 | 
			
			
				+    this.endWith = this.props.value 
			 | 
		
	
		
			
			| 
				
			 | 
			
				91
			 | 
			
			
				+    this.dirty = true 
			 | 
		
	
		
			
			| 
				
			 | 
			
				92
			 | 
			
			
				+    this.startAnimate() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				93
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				94
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				95
			 | 
			
			
				+  componentWillUpdate(nextProps, nextState) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				96
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				97
			 | 
			
			
				+    // check if start an animation 
			 | 
		
	
		
			
			| 
				
			 | 
			
				98
			 | 
			
			
				+    if(this.props.value !== nextProps.value) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				99
			 | 
			
			
				+      this.startFrom = this.props.value 
			 | 
		
	
		
			
			| 
				
			 | 
			
				100
			 | 
			
			
				+      this.endWith = nextProps.value 
			 | 
		
	
		
			
			| 
				
			 | 
			
				101
			 | 
			
			
				+      this.dirty = true 
			 | 
		
	
		
			
			| 
				
			 | 
			
				102
			 | 
			
			
				+      this.startAnimate() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				103
			 | 
			
			
				+      return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				104
			 | 
			
			
				+    } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				105
			 | 
			
			
				+    // Check if iterate animation frame 
			 | 
		
	
		
			
			| 
				
			 | 
			
				106
			 | 
			
			
				+    if(!this.dirty) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				107
			 | 
			
			
				+      return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				108
			 | 
			
			
				+    } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				109
			 | 
			
			
				+    if (this.direction === true) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				110
			 | 
			
			
				+      if(parseFloat(this.state.value) <= parseFloat(this.props.value)) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				111
			 | 
			
			
				+        this.startAnimate(); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				112
			 | 
			
			
				+      } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				113
			 | 
			
			
				+    } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				114
			 | 
			
			
				+    else if(this.direction === false){ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				115
			 | 
			
			
				+      if (parseFloat(this.state.value) >= parseFloat(this.props.value)) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				116
			 | 
			
			
				+        this.startAnimate(); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				117
			 | 
			
			
				+      } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				118
			 | 
			
			
				+    } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				119
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				120
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				121
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				122
			 | 
			
			
				+  render() { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				123
			 | 
			
			
				+    return ( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				124
			 | 
			
			
				+      <Text {...this.props}> 
			 | 
		
	
		
			
			| 
				
			 | 
			
				125
			 | 
			
			
				+        {this.state.displayValue} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				126
			 | 
			
			
				+      </Text>) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				127
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				128
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				129
			 | 
			
			
				+  startAnimate() { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				130
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				131
			 | 
			
			
				+    let progress = this.getAnimationProgress() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				132
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				133
			 | 
			
			
				+    Timer.setTimeout(() => { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				134
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				135
			 | 
			
			
				+      let value = (this.endWith - this.startFrom)/this.props.steps 
			 | 
		
	
		
			
			| 
				
			 | 
			
				136
			 | 
			
			
				+      if(this.props.countBy) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				137
			 | 
			
			
				+        value = Math.sign(value)*Math.abs(this.props.countBy) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				138
			 | 
			
			
				+      let total = parseFloat(this.state.value) + parseFloat(value) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				139
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				140
			 | 
			
			
				+      this.direction = (value > 0) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				141
			 | 
			
			
				+      // animation terminate conditions 
			 | 
		
	
		
			
			| 
				
			 | 
			
				142
			 | 
			
			
				+      if (((this.direction) ^ (total <= this.endWith)) === 1) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				143
			 | 
			
			
				+        this.dirty = false 
			 | 
		
	
		
			
			| 
				
			 | 
			
				144
			 | 
			
			
				+        total = this.endWith 
			 | 
		
	
		
			
			| 
				
			 | 
			
				145
			 | 
			
			
				+        this.props.onFinish(total, this.props.formatter(total)) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				146
			 | 
			
			
				+      } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				147
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				148
			 | 
			
			
				+      if(this.props.onProgress) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				149
			 | 
			
			
				+        this.props.onProgress(this.state.value, total) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				150
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				151
			 | 
			
			
				+      this.setState({ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				152
			 | 
			
			
				+        value : total, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				153
			 | 
			
			
				+        displayValue : this.props.formatter(total) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				154
			 | 
			
			
				+      }) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				155
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				156
			 | 
			
			
				+    }, this.getTimingFunction(this.props.interval, progress)) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				157
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				158
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				159
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				160
			 | 
			
			
				+  getAnimationProgress():number { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				161
			 | 
			
			
				+    return (this.state.value - this.startFrom) / (this.endWith - this.startFrom) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				162
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				163
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				164
			 | 
			
			
				+  getTimingFunction(interval:number, progress:number) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				165
			 | 
			
			
				+    if(typeof this.props.timing === 'string') { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				166
			 | 
			
			
				+      let fn = AnimateNumber.TimingFunctions[this.props.timing] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				167
			 | 
			
			
				+      return fn(interval, progress) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				168
			 | 
			
			
				+    } else if(typeof this.props.timing === 'function') 
			 | 
		
	
		
			
			| 
				
			 | 
			
				169
			 | 
			
			
				+      return this.props.timing(interval, progress) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				170
			 | 
			
			
				+    else 
			 | 
		
	
		
			
			| 
				
			 | 
			
				171
			 | 
			
			
				+      return AnimateNumber.TimingFunctions['linear'](interval, progress) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				172
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				173
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				174
			 | 
			
			
				+} 
			 |