fade out splash screen in react-native - react-native

Below is my code, I try to fade out an image and after fade out go to the login page.
It goes to login page but the animation is not working.
The image comes and wait then disappears immediately. What I am doing wrong ?
state={
fadeAmin: new Animated.Value(0),
}
componentDidMount() {
this.setState({ fadeAnim: new Animated.Value(1) },
() => {
Animated.timing( // Animate over time
this.state.fadeAnim, // The animated value to drive
{
toValue: 0, // Animate to opacity: 1 (opaque)
duration: 2000,
}
).start();
})
}
componentWillMount() {
setInterval(() => {
this.props.navigation.navigate('login');
},2000) // Starts the animation
}
render() {
let { fadeAnim } = this.state;
console.log('props', this.props)
return (
<View style = {{flex:1 , alignItems:"center", justifyContent:'center'}}>
<Login navigation={this.props.navigation}/>
<Animated.View style={{ ...this.props.style, opacity: fadeAnim }} >
{this.props.children}
<Image style= {styles.logo} source={require('../../image/dataLogo.jpeg')} />
</Animated.View>
</View>
);
}
}

If I understood you correctly, after the fadeout you want to navigate to the login screen, in that case I am guessing the issue is with lifecycle methods.
So componentWillMount calls before componentDidMount, now even though you gave setTimeout (you really dont need that) its about exact time as your fade animation.
So to fix this I would suggest remove the componentWillMount and do all the logic in componentDidMount. The start takes a call back, it will be called after the animation is finished, you can take this opportunity to navigate wherever you want.
Also if you want additional time, add setTimeout then navigate.
componentDidMount() {
Animated.timing( // Animate over time
this.fadeAnim, // The animated value to drive
{
toValue: 0, // Animate to opacity: 1 (opaque)
duration: 2000,
}
).start(() => {
console.log('fading out');
// this.props.navigation.navigate('login');
/* setTimeout(() => {
this.fadeOut();
}, 2000); */
});
}
Example, https://snack.expo.io/SkFnm_x8E

Related

Simple animation in react-native: moving an image from left to right

Can anyone share a code example for a react-native animation that moves an image from left to right, then move back to the starting point and repeat the motion?
Update:
The answer below helped a lot, but it didn't work. I used it to create the following animation that moves an image from left to right (I am using RN 0.62.2).
import React from 'react'
import { StyleSheet, View, Animated, Easing } from 'react-native';
const test = require('../images/test.png');
export class Splash extends React.Component {
constructor(props) {
super(props);
this.state = { xValue: new Animated.Value(-100) }
}
moveLR = () => {
Animated.timing(
this.state.xValue,
{
toValue: 100,
duration: 1000, // the duration of the animation
easing: Easing.linear, // the style of animation
useNativeDriver: true
}
).start();
}
moveRL = () => {
Animated.timing(
this.state.xValue,
{
toValue: -100,
duration: 3000, // the duration of the animation
easing: Easing.linear, // the style of animation
useNativeDriver: true
}
).start();
}
componentDidMount = () => {
this.moveLR();
}
render = () => {
return (
<View style={styles.mainContainer}>
<Animated.Image
style={{ width: 170, height: 146 }}
source={test}
style={{ width: 170, height: 146,
transform: [{ translateX: this.state.xValue }] }}>
</Animated.Image>
</View>
)
}
}
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
animationView: {
backgroundColor: 'red',
width: 100,
height: 100
}
})
Update by Yossi:
The code below didn't work for me in RN 0.62.2. I am accepting the answer, but I modified it and the code that is working is included now in the question.
Original answer:
Before getting started, I need to introduce you to the two types of values ​​for Animated animations:
Animated.Value () where we define a value, useful when we want to move an element on a single axis (X or Y), change the size of an element, etc. This is what we will use here, in this chapter, and this is what is used the most.
Animated.ValueXY () where we define a vector, useful for moving an element on two axes.
With these values, we can define several types of Animated animations. We will discover them one by one, testing them each time. in this example, I will only talk about Animated.timing ()
Here you can see an example of code which is gonna moove a red box from left to right and stop when the user decides, you can try it and tell if it worked for you :
// Test.js
import React from 'react'
import { StyleSheet, View, Animated, TouchableOpacity, Text, Easing } from 'react-native'
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
leftPosition : new Animated.Value (0)
}
this.mooveLR = this.mooveLR.bind(this);
this.mooveRL = this.mooveRL.bind(this);
this.stopAnimation = this.stopAnimation.bind(this);
}
stopAnimation () {
this.setState({
leftPosition : this.state.leftPosition // this forces the left position to remain the same considering the `componentDidMount` method already happened
})
}
mooveLR (){
Animated.timing(
this.state.leftPosition,
{
toValue: 100,
duration: 3000, // the duration of the animation
easing: Easing.linear, // the style of animation
}
).start() // starts this annimation once this method is called
}
mooveRL (){
Animated.timing(
this.state.leftPosition,
{
toValue: 0,
duration: 3000, // the duration of the animation
easing: Easing.linear, // the style of animation
}
).start() // starts this annimation once this method is called
}
componentDidMount(){
this.state.leftPosition === 0 ? this.mooveLR () : this.mooveRL () // repeats always when the red box return to its initial position : leftPosition === 0
}
render() {
return (
<View style={styles.main_container}>
<Animated.View style={[styles.animation_view, {left : this.state.leftPosition}]}>
</Animated.View>
<TouchableOpacity onPress = { () => this.stopAnimation ()}>
<Text>Stop animation</Text>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
main_container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
animation_view: {
backgroundColor: 'red',
width: 100,
height: 100
}
})
export default Test;
Hope it's gonna help
Regards
I have resolved this issue by doing:
import {
Easing
} from 'react-native';
Since I prefer using react hooks, I converted the solution Provided by #pacy.eth, and here it is for anyone who prefers to use react hooks.
import { View, Text, Animated, Easing } from 'react-native';
import React, { useEffect, useState } from 'react';
const Animate= ({ children, left, leftP, duration }) => {
const [leftPosition, setLeftPosition] = useState(new Animated.Value (leftP));
useEffect(() => {
left ? mooveLR() : mooveRL();
}, []);
const mooveLR = () => {
Animated.timing(leftPosition, {
toValue: 100,
duration, // the duration of the animation
easing: Easing.linear, // the style of animation
}).start(); // starts this annimation once this method is called
};
const mooveRL = () => {
Animated.timing(leftPosition, {
toValue: 0,
duration, // the duration of the animation
easing: Easing.linear, // the style of animation
}).start(); // starts this annimation once this method is called
};
return (
<Animated.View style={[{ left: leftPosition }]}>{children}</Animated.View>
);
};
export default Animate;
And with a little modifications, I made it reusable in severals ways:
one of my favorite ways, is by wrapping the component that I want to animate and I pass the the direction "left: true or false" I set the "leftP" which is the leftPosition (in my case I am hiding the view and with a click of a button I slide it in with the Animate component created) and the "duration" of the animation.
for ex:
<Animate left={false} duration={1000} leftP={-Dimensions.get('window').width}>
...
</Animate>

Maximum update Depth Exceeded - wrapping animated component in a modal?

Having a problem here. Essentially trying to create an animation for a modal pop up when a button is tapped.
Because I have a tab bar in the application, To let the modal pop up over the tab bar, I need to wrap the Animated Component in a Modal (or maybe I don't? Would be good to know other solutions). This means that when I toggle the animation, I also want to toggle local state for the modal's visibility.
However, when I hit the button, the component updates through redux and calls the toggleModal function - the inclusion of this.setState(prevState => ({ modalVisible: !prevState.modalVisible })) is somehow causing a repetitive call of setState.
I have the animation working in a throwaway expo app without useNativeDriver and without a tab bar (thus not needing a modal component to wrap the animation).
How can I begin to fix this?
AddCircleModal:
const screenHeight = Dimensions.get("window").height
class AddCircleModal extends React.Component {
state = {
top: new Animated.Value(screenHeight),
modalVisible: false
}
componentDidUpdate() {
('modalDidUpdate')
this.toggleModal()
}
toggleModal = () => {
console.log('toggled')
this.setState(prevState => ({ modalVisible: !prevState.modalVisible })) //if I have this line I'm getting 'maximum update depth exceeded' error
if (this.props.action === 'openModal') {
Animated.spring(this.state.top, {
toValue: 174,
useNativeDriver: true
}).start()
}
if (this.props.action === 'closeModal') {
Animated.spring(this.state.top, {
toValue: screenHeight,
useNativeDriver: true
}).start()
}
}
render() {
return (
<Modal
transparent={true}
visible={this.state.modalVisible}
>
<AnimatedContainer style={{scaleY: this.state.top}}>
<TouchableOpacity
onPress={this.props.closeModal}
style={{ position: "absolute", top: 120, left: "50%", marginLeft: -22, zIndex: 1 }}
>
<Text>GO</Text>
</TouchableOpacity>
</AnimatedContainer>
</Modal>
)
}
}
const Container = styled.View`
position: absolute;
background: white;
width: 100%;
height: 100%;
z-index: 100;
border-radius: 22px;
`
const AnimatedContainer = Animated.createAnimatedComponent(Container)
function mapStateToProps(state) {
return { action: state.action }
}
function mapDispatchToProps(dispatch) {
return {
closeModal: () =>
dispatch({
type: "CLOSE_MODAL"
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddCircleModal)

React Native Splash Screen

I am trying to create a Splash Screen that loads first when the app starts. I am creating it with redux persist. The initial state is the Splash screen. The Splash has a function to check if its first time running. The setTopLevelNavigator redirects to the persisted screen. After the Splash Screen, it should direct to the persisted screen. I am not sure on how I can implement to load the splash first. Any help would be great!
render() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<AppWithNavigationState
ref={ref => setTopLevelNavigator(ref)}
/>
</PersistGate>
</Provider>
);
}
This is the Splash Screen
class SplashScreen extends Component {
constructor(props) {
super(props);
this.state = {
fadeAnim: new Animated.Value(0),
};
}
async componentDidMount() {
const {
settings,
navigation,
} = this.props;
if (settings.firstRun) {
const { fadeAnim } = this.state;
Animated.timing(
fadeAnim,
{
toValue: 1,
duration: 2000,
},
).start();
} else {
const { fadeAnim } = this.state;
fadeAnim.setValue(1);
Animated.timing(
fadeAnim,
{
toValue: 0.01,
duration: 1000,
easing: Easing.inOut(Easing.ease),
},
).start();
setTimeout(() => {
navigation.replace('Home');
}, 1000);
}
}
onScroll =() => {
const { navigation } = this.props;
navigation.navigate('Intro');
}
render() {
const { fadeAnim } = this.state;
return (
<TouchableWithoutFeedback
onPress={this.onScroll}
>
<View style={styles.container}>
{ Platform.OS === 'ios'
? <StatusBar barStyle="light-content" />
: <StatusBar hidden />
}
<ScrollView
horizontal
onMomentumScrollBegin={this.onScroll}
>
<AnimateImage
fadeAnim={fadeAnim}
/>
</ScrollView>
</View>
</TouchableWithoutFeedback>
);
}
}
Just set the SplashScreen component to the loading prop.
<PersistGate loading={<SplashScreen />} persistor={persistor}>
My opinion is that you should use a module such as react-native-splash-screen to hide the native splash screen when you need to. This will give you a much smoother result when starting your app, cause the user will only see the launch screen and then your react native app, while in your current way the user sees the default launch screen, then a white screen and then your Splash Screen. I know that this is not how it should work, but the transition between the launch screen and the react native app is unfortunately not smooth at all.
Basically you need to
Create the launch screen assets (see this tutorial for more information)
Add the npm module mentioned above
Pass the SplashScreen you already created in the PersistGate loading attribute like this: loading={SplashScreen}
The SplashScreen component you created doesn't need to render
anything and you can hide the native splash screen on component
unmount
componentWillUnmount() {
SplashScreen.hide();
}

React-native scroll to top with pull to refresh iOS

I have a react-native app, that I want to scroll to top when the FlatList is refreshing in iOS. I can scroll to the top of the FlaList by using:
this.componentRef.FlatList.scrollToOffset({x: 0, y: 0, animated: true});
Now, the list has Pull-to-refresh on, so I would like to scroll above the refreshing indicator in iOS.
I tried:
this.componentRef.FlatList.scrollToOffset({x: 0, y: -20, animated: true});
and declaring the RefreshControl with a ref as refreshcontrol (using callback ref declaration):
this.componentRef.FlatList.refreshcontrol.scrollToOffset({x: 0, y: 0, animated: true});
and
this.componentRef.refreshcontrol.scrollToOffset({x: 0, y: 0, animated: true});
But none work. Is anyone aware of a way I can scroll above the refreshing indicator, if its on? This only happens in iOS as Android's refreshing indicator works differently.
UPDATE:
As scrollToOffset is not available for the RefrehControl component, it won;t work. Which brings me back to how can I scroll above a RefreshControl in a FlatList. My last attempt:
this.FlatList.scrollToOffset({x: 0, y: 0, animated: true});
Still scrolls to the beginning of the list, yet the "RefreshControl" is visually hidden.
I also tried adding an empty ScrollView above and scrolling to it, because it is empty, it did not work. Any ideas?
UPDATE 2
To clarify, this is how everything is called (simplified):
_scrollAndRefresh method in Main.js:
_scrollAndRefresh = () => {
this.setState({
loading: true
}, () => {
this.CustomFlatList._scrollToTop();
});
}
Rendering the component in Main.js:
<CustomFlatList ref={(ref) => {
this.CustomFlatList = ref}}
onRefresh={this._handleRefresh} loading={this.state.loading}/>
_handleRefresh method in Main.js:
_handleRefresh = () => {
this.setState({
loading: true
}, () => {
// REFRESH ACTION
})
};
_scrollToTop method CustomFlatList.js:
_scrollToTop = () => {
if (Platform.OS === 'ios' && this.props.loading) {
this.FlatList.scrollToOffset({x: 0, y: 0, animated: true});
}
else {
this.FlatList.scrollToOffset({x: 0, y: 0, animated: true});
}
}
And FlatList CustomFlatList.js:
<FlatList
ref={(ref) => { this.FlatList = ref; }}
refreshControl={<RefreshControl
refreshing={this.props.loading}
onRefresh={this.props.onRefresh}
/>}
/>
Since <RefreshControl /> detect refresh behavior from gesture, <FlatList /> scroll method has no effect on it; And you are just attempt to hack it.
Suggest to do it this way. You still scroll to top and shows refresh, and more directly:
constructor(props) {
super(props);
this.state = {
refreshing: false,
}
}
/// scroll to top, and show refresh at the same time
scrollToTopAndRefresh() {
this.componentRef.FlatList.scrollToOffset({x: 0, y: 0, animated: true});
this.setState({
refreshing: true,
}, () => {
this.refresh();
});
}
refresh() {
/// put your refresh logic here
}
componentRef = {};
render() {
return (
<FlatList ref={ (ref) => this.componentRef.FlatList = ref }
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={() => this.refresh()}
/>
}
/>
);
}
Update 2:
I made a simple workable code for your needs.
import React, { Component } from 'react';
import {
Image,
View,
FlatList,
Text,
StyleSheet,
Button,
RefreshControl,
} from 'react-native';
export class App extends Component {
constructor(props) {
super(props);
this.scrollToTopAndRefresh = this.scrollToTopAndRefresh.bind(this);
this.doRefresh = this.doRefresh.bind(this);
this.state = {
refreshing: false,
}
}
scrollToTopAndRefresh() {
this.flatlistref.scrollToOffset({y: 0, animated: true});
this.setState({refreshing: true}, this.doRefresh);
}
doRefresh() {
/// do refresh work here /////
//////////////////////////////
setTimeout( () => this.setState({refreshing: false}), 1000);
}
flatlistref = null;
render() {
return (
<View style={{flex: 1}}>
<FlatList
ref={(ref) => this.flatlistref = ref}
data={Array(30).fill(1)}
renderItem={() => <Text style={styles.line}>This is one line.</Text>}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.doRefresh}
/>
}
/>
<Button title='Scroll To Top' onPress={this.scrollToTopAndRefresh} />
</View>
)
}
}
const styles = StyleSheet.create({
line: {
height: 50,
paddingTop: 17,
textAlign: 'center',
backgroundColor: 'orange',
borderWidth: 1,
borderColor: 'purple',
}
});
Result:
I have something like this in my app:
<FlatList
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={() => this._onRefresh()}
/>}
data=...
/>
Maybe something like this would work:
this.componentRef.FlatList.scrollToOffset({x: 0, y: 0, animated: true});
this.setState({refreshing: true});
this._onRefresh();
this.setState({refreshing: false});

React Native Animations simply not running

My issue with React Native seems to be project wide. Anything using the Animated API simply does not run. I am running React Native 0.49.2,
Nothing seems to be working, I have tried out several peoples code, with nothing ever happening. The issue to me seems to be whenever I call "Animated.Timing().start();" it never actually starts. Heres some short example code:
class Splash extends React.Component {
constructor(props){
super(props);
this.state = {
ShowSetup: true,
fadeAnim: new Animated.Value(0), // Initial value for opacity: 0
};
}
componentDidMount(){
this.Animator()
}
Animator(){
console.log("ANIMATOR RUNNING!")
Animated.timing( // Animate over time
this.state.fadeAnim, // The animated value to drive
{
toValue: 1, // Animate to opacity: 1 (opaque)
duration: 10000, // Make it take a while
}
).start(); // Starts the animation
}
render() {
let { fadeAnim } = this.state;
return (
<View style={{flex: 1, backgroundColor: 'blue'}}>
<Animated.View // Special animatable View
style={{
opacity: fadeAnim, // Bind opacity to animated value
}}
>
<Text>Fade In</Text>
</Animated.View>
</View>
);
}
}
No matter how long you wait, the value will not show up. Has become a big head scratcher for me as I am not sure what else to do
try this:
<View style={{flex: 1, backgroundColor: 'blue'}}>
<Animated.View // Special animatable View
style={{
opacity: this.state.fadeAnim, // Bind opacity to animated value
}}
>
<Text>Fade In</Text>
</Animated.View>
</View>
I think the fadeAnim variable you declare at the top of your render isn't being reassigned
-- EDIT --
Here is some animation code I've used, the render is implemented the same way you have it.
constructor() {
super();
this.state = {
topBoxOpacity: new Animated.Value(0),
leftBoxOpacity: new Animated.Value(0),
mainImageOpacity: new Animated.Value(0),
}
}
componentDidMount() {
const timing = Animated.timing;
Animated.parallel([
timing(this.state.leftBoxOpacity, {
toValue: 1,
duration: 700,
delay: 700,
}),
timing(this.state.topBoxOpacity, {
toValue: 1,
duration: 700,
delay: 1400,
}),
timing(this.state.mainImageOpacity, {
toValue: 1,
duration: 700,
delay: 2100,
}),
]).start();
}