react-native-snow
Version:
Fullscreen snow overlay component for react native
173 lines (153 loc) • 4.29 kB
JavaScript
import React, { Component } from 'react';
import {
StyleSheet,
Dimensions,
Animated,
Easing,
AppState
} from 'react-native';
import PropTypes, { shape } from 'prop-types';
const HEIGHT = Dimensions.get('window').height;
const topOffset = HEIGHT * .1;
const windowHeight = HEIGHT + topOffset;
export default class Snowflake extends Component {
_fallAnimation = null;
_shakeAnimation = null;
constructor(props) {
super(props);
this.state = {
glyph: props.glyph || '❅',
visible: props.visible || false,
size: props.size || 12,
amplitude: props.amplitude || 60, //80,
fallDuration: props.fallDuration || 10000,
shakeDuration: props.shakeDuration || 4000,
fallDelay: props.fallDelay || 0,
shakeDelay: props.shakeDelay || 0,
offset: props.offset || 0,
translateY: new Animated.Value(0),
translateX: new Animated.Value(0),
appState: AppState.currentState,
};
}
_handleAppStateChange = (nextAppState) => {
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
// App is coming into foreground
this._initAnimation();
}
if (this.state.appState.match(/^active|foreground/) && nextAppState === 'inactive') {
// App is going into background
this._stopAnimation();
}
this.setState({appState: nextAppState});
}
_stopAnimation() {
if (this._fallAnimation) {
this._fallAnimation.stop();
this._fallAnimation = null;
this.setState({
translateY: new Animated.Value(0),
});
}
if (this._shakeAnimation) {
this._shakeAnimation.stop();
this._shakeAnimation = null;
this.setState({
translateX: new Animated.Value(0),
});
}
}
_initAnimation() {
this._fallAnimation = Animated.loop(
Animated.timing(
this.state.translateY,
{
toValue: 1,
easing: Easing.linear,
duration: this.state.fallDuration,
useNativeDriver: true,
}
)
);
this._shakeAnimation = Animated.loop(
Animated.sequence([
Animated.timing(
this.state.translateX,
{
toValue: 1,
easing: Easing.easeInOutSine,
duration: this.state.shakeDuration / 2,
useNativeDriver: true,
}
),
Animated.timing(
this.state.translateX,
{
toValue: 0,
easing: Easing.easeInOutSine,
duration: this.state.shakeDuration / 2,
useNativeDriver: true,
}
)
])
);
setTimeout( () => {
this._fallAnimation && this._fallAnimation.start();
}, this.state.fallDelay);
setTimeout( () => {
this._shakeAnimation && this._shakeAnimation.start();
}, this.state.shakeDelay);
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
this._initAnimation();
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
render() {
const { style } = this.props;
const translateX = this.state.translateX.interpolate({
inputRange: [0, 1],
outputRange: [0, this.state.amplitude]
});
const translateY = this.state.translateY.interpolate({
inputRange: [0, 1],
outputRange: [0, windowHeight]
});
return (
<Animated.Text style={[styles.text, {
fontSize: this.state.size,
left: this.state.offset,
transform: [{translateX}, {translateY}]
}, style]}>
{this.state.glyph}
</Animated.Text>
);
}
}
Snowflake.propTypes = {
glyph: PropTypes.string,
visible: PropTypes.bool,
size: PropTypes.number,
offset: PropTypes.string,
amplitude: PropTypes.number,
fallDuration: PropTypes.number,
shakeDuration: PropTypes.number,
fallDelay: PropTypes.number,
shakeDelay: PropTypes.number,
style: shape({
color: PropTypes.string,
}),
};
const styles = StyleSheet.create({
text: {
top: -topOffset,
height: windowHeight,
color: 'white',
backgroundColor: 'transparent',
textShadowColor: 'rgba(0, 0, 0, 0.3)',
textShadowOffset: {width: 1, height: 1},
textShadowRadius: 1,
}
});