Created
September 23, 2016 13:28
-
-
Save vaxxis/bdce8eecd726c47160a8c5d4cc000031 to your computer and use it in GitHub Desktop.
React Native Animate Number
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import { View, Text } from 'react-native'; | |
import Timer from 'react-timer-mixin'; | |
const HALF_RAD = Math.PI/2 | |
export default class AnimateNumber extends Component { | |
props : { | |
countBy? : ?number, | |
interval? : ?number, | |
steps? : ?number, | |
value : number, | |
timing : 'linear' | 'easeOut' | 'easeIn' | () => number, | |
formatter : () => {}, | |
onProgress : () => {}, | |
onFinish : () => {} | |
}; | |
static defaultProps = { | |
interval : 14, | |
timing : 'linear', | |
steps : 45, | |
value : 0, | |
formatter : (val) => val, | |
onFinish : () => {} | |
}; | |
static TimingFunctions = { | |
linear : (interval:number, progress:number):number => { | |
return interval | |
}, | |
easeOut : (interval:number, progress:number):number => { | |
return interval * Math.sin(HALF_RAD*progress) * 5 | |
}, | |
easeIn : (interval:number, progress:number):number => { | |
return interval * Math.sin((HALF_RAD - HALF_RAD*progress)) * 5 | |
}, | |
}; | |
state : { | |
value? : ?number, | |
displayValue? : ?number | |
}; | |
/** | |
* Animation direction, true means positive, false means negative. | |
* @type {bool} | |
*/ | |
direction : bool; | |
/** | |
* Start value of last animation. | |
* @type {number} | |
*/ | |
startFrom : number; | |
/** | |
* End value of last animation. | |
* @type {number} | |
*/ | |
endWith : number; | |
constructor(props:any) { | |
super(props); | |
this.dirty = false; | |
this.startFrom = 0; | |
this.endWith = 0; | |
this.state = { | |
value : 0, | |
displayValue : 0 | |
}; | |
} | |
componentDidMount() { | |
this.startFrom = this.state.value | |
this.endWith = this.props.value | |
this.dirty = true | |
this.startAnimate() | |
} | |
componentWillUpdate(nextProps, nextState) { | |
// check if start an animation | |
if(this.props.value !== nextProps.value) { | |
this.startFrom = this.props.value | |
this.endWith = nextProps.value | |
this.dirty = true | |
this.startAnimate() | |
return | |
} | |
// Check if iterate animation frame | |
if(!this.dirty) { | |
return | |
} | |
if (this.direction === true) { | |
if(parseInt(this.state.value) <= parseInt(this.props.value)) { | |
this.startAnimate(); | |
} | |
} | |
else if(this.direction === false){ | |
if (parseInt(this.state.value) >= parseInt(this.props.value)) { | |
this.startAnimate(); | |
} | |
} | |
} | |
render() { | |
return ( | |
<Text {...this.props}> | |
{this.state.displayValue} | |
</Text>) | |
} | |
startAnimate() { | |
let progress = this.getAnimationProgress() | |
Timer.setTimeout(() => { | |
let value = (this.endWith - this.startFrom)/this.props.steps | |
if(this.props.countBy) | |
value = Math.sign(value)*Math.abs(this.props.countBy) | |
let total = parseInt(this.state.value) + parseInt(value) | |
this.direction = (value > 0) | |
// animation terminate conditions | |
if (((this.direction) ^ (total <= this.endWith)) === 1) { | |
this.dirty = false | |
total = this.endWith | |
this.props.onFinish(total, this.props.formatter(total)) | |
} | |
if(this.props.onProgress) | |
this.props.onProgress(this.state.value, total) | |
this.setState({ | |
value : total, | |
displayValue : this.props.formatter(total) | |
}) | |
}, this.getTimingFunction(this.props.interval, progress)) | |
} | |
getAnimationProgress():number { | |
return (this.state.value - this.startFrom) / (this.endWith - this.startFrom) | |
} | |
getTimingFunction(interval:number, progress:number) { | |
if(typeof this.props.timing === 'string') { | |
let fn = AnimateNumber.TimingFunctions[this.props.timing] | |
return fn(interval, progress) | |
} else if(typeof this.props.timing === 'function') | |
return this.props.timing(interval, progress) | |
else | |
return AnimateNumber.TimingFunctions['linear'](interval, progress) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment