Skip to content

Instantly share code, notes, and snippets.

@wachunei
Last active December 12, 2019 06:01
Show Gist options
  • Save wachunei/79a576f9a80f135274c396d8d98e2f51 to your computer and use it in GitHub Desktop.
Save wachunei/79a576f9a80f135274c396d8d98e2f51 to your computer and use it in GitHub Desktop.
Replicating Twitter iOS splash screen animation on React Native during redux rehydration
import React, { Component } from 'react';
import { Animated, Easing, StatusBar, MaskedViewIOS, View } from 'react-native';
class AnimatedGate extends Component {
constructor(props) {
super(props);
this.beginAnimation = this.beginAnimation.bind(this);
this.state = {
animation: new Animated.Value(0),
};
}
componentDidMount() {
StatusBar.setHidden(true);
}
componentWillReceiveProps(nextProps) {
if (!this.props.bootstraped && nextProps.bootstraped) {
setTimeout(() => this.beginAnimation(), 1000);
}
}
beginAnimation() {
const duration = 1300;
setTimeout(() => StatusBar.setHidden(false, 'fade'), 0.6 * duration);
Animated.timing(this.state.animation, {
toValue: 100,
duration,
easing: Easing.bezier(0.42, 0, 0.42, 1),
}).start();
}
render() {
const { animation } = this.state;
const opacity = animation.interpolate({
inputRange: [0, 30, 70],
outputRange: [0, 0, 1],
extrapolate: 'clamp',
});
const MainApp = (
<View style={styles.mainContainer}>
<Animated.View
style={{
flex: 1,
opacity,
transform: [
{
scale: animation.interpolate({
inputRange: [0, 50, 100],
outputRange: [1.05, 1.05, 1],
}),
},
],
}}
>
{this.props.children}
</Animated.View>
</View>
);
const MaskElement = (
<View style={styles.maskElementContainer}>
<Animated.Image
source={require('./twitter_logo.png')}
style={[
styles.logo,
{
transform: [
{
scale: animation.interpolate({
inputRange: [0, 30, 100],
outputRange: [1, 0.8, 40],
extrapolate: 'clamp',
}),
},
],
},
]}
/>
</View>
);
return (
<View style={styles.gateContainer}>
<MaskedViewIOS style={styles.maskedView} maskElement={MaskElement}>
{MainApp}
</MaskedViewIOS>
</View>
);
}
}
const styles = {
gateContainer: {
flex: 1,
backgroundColor: 'rgb(29, 161, 242)',
},
mainContainer: {
flex: 1,
backgroundColor: 'white',
},
maskedView: {
flex: 1,
},
maskElementContainer: {
flex: 1,
backgroundColor: 'transparent',
justifyContent: 'center',
alignItems: 'center',
},
logo: {
width: 62,
height: 62,
},
};
export default AnimatedGate;
# App is a regular redux app, but PersistGate
# takes a function as a child component.
# (see https://github.com/rt2zz/redux-persist/pull/718)
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/src/integration/react';
import Main from './Main';
import AnimatedGate from './AnimatedGate';
import configureStore from './configureStore';
const { store, persistor } = configureStore();
export default class App extends Component {
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
{bootstraped => (
<AnimatedGate bootstraped={bootstraped}>
<Main />
</AnimatedGate>
)}
</PersistGate>
</Provider>
);
}
}
# Twitter UI mockup (is an image)
import React from 'react';
import { View, Image, StatusBar } from 'react-native';
const Main = () => (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<Image
source={require('./twitterimage.jpeg')}
style={styles.image}
resizeMode="contain"
/>
</View>
);
const styles = {
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
image: {
flex: 1,
},
};
export default Main;
@ronak301
Copy link

How to replicate this on android ?

@brunocavallarigois
Copy link

Please, you can share the 'configureStore' file and ours references?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment