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>
Related
Rotation is a style transform and in RN, you can rotate things like this
render() {
return (
<View style={{transform:[{rotate: '10 deg'}]}}>
<Image source={require('./logo.png')} />
</View>
);
}
However, to animate things in RN, you have to use numbers, not strings. Can you still animate transforms in RN or do I have to come up with some kind of sprite sheet and change the Image src at some fps?
You can actually animate strings using the interpolate method. interpolate takes a range of values, typically 0 to 1 works well for most things, and interpolates them into a range of values (these could be strings, numbers, even functions that return a value).
What you would do is take an existing Animated value and pass it through the interpolate function like this:
spinValue = new Animated.Value(0);
// First set up animation
Animated.timing(
this.spinValue,
{
toValue: 1,
duration: 3000,
easing: Easing.linear, // Easing is an additional import from react-native
useNativeDriver: true // To make use of native driver for performance
}
).start()
// Next, interpolate beginning and end values (in this case 0 and 1)
const spin = this.spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
})
Then use it in your component like this:
<Animated.Image
style={{transform: [{rotate: spin}] }}
source={{uri: 'somesource.png'}} />
In case if you want to do the rotation in loop, then add the Animated.timing in the Animated.loop
Animated.loop(
Animated.timing(
this.spinValue,
{
toValue: 1,
duration: 3000,
easing: Easing.linear,
useNativeDriver: true
}
)
).start();
Don't forget to add property useNativeDriver to ensure that you get the best performance out of this animation:
// First set up animation
Animated.timing(
this.state.spinValue,
{
toValue: 1,
duration: 3000,
easing: Easing.linear,
useNativeDriver: true
}
).start();
A note for the newbies like me:
For animating something else you need to wrap it in <Animated.SOMETHING> for this to work. Or else the compiler will panic on that transform property:
import {Animated} from 'react-native';
...
//animation code above
...
<Animated.View style={{transform: [{rotate: spinValue}] }} >
<YourComponent />
</Animated.View>
BUT for an image (Animated.Image), the example above is 100% goodness and correct.
Since most of the answers are functions & hooks based, herewith a complete example of class based Animation of Image.
import React from 'react';
import {
SafeAreaView,
View,
Animated,
Easing,
TouchableHighlight,
Text,
} from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
rotateValueHolder: new Animated.Value(0)
};
}
componentDidMount = () => {
this.startImageRotateFunction();
}
startImageRotateFunction = () => {
Animated.loop(Animated.timing(this.state.rotateValueHolder, {
toValue: 1,
duration: 3000,
easing: Easing.linear,
useNativeDriver: false,
})).start();
};
render(){
return(
<SafeAreaView>
<View>
<Animated.Image
style={{
width: 200,
height: 200,
alignSelf:"center",
transform:
[
{
rotate: this.state.rotateValueHolder.interpolate(
{
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
}
)
}
],
}}
source={{uri:'https://raw.githubusercontent.com/AboutReact/sampleresource/master/old_logo.png',}}
/>
<TouchableHighlight
onPress={() => this.startImageRotateFunction()}>
<Text style={{textAlign:"center"}}>
CLICK HERE
</Text>
</TouchableHighlight>
</View>
</SafeAreaView>
);
}
}
Just gonna drop the solution I solved by stitching together parts from the answers here.
import { Feather } from '#expo/vector-icons'
import * as React from 'react'
import { TextStyle, Animated, Easing } from 'react-native'
import { Colors, FontSize } from '~/constants/Theme'
export const LoadingSpinner = React.memo(
({ color = Colors['sand'], size = FontSize['md'] - 1, fadeInDelay = 1000, ...props }: Props) => {
const fadeInValue = new Animated.Value(0)
const spinValue = new Animated.Value(0)
Animated.sequence([
Animated.delay(fadeInDelay),
Animated.timing(fadeInValue, {
toValue: 1,
duration: 1500,
easing: Easing.linear,
useNativeDriver: true,
}),
]).start()
Animated.loop(
Animated.timing(spinValue, {
toValue: 360,
duration: 300000,
easing: Easing.linear,
useNativeDriver: true,
})
).start()
return (
<Animated.View
style={{
opacity: fadeInValue,
transform: [{ rotate: spinValue }],
}}
>
<Feather
name="loader"
size={size}
style={{
color,
alignSelf: 'center',
}}
{...props.featherProps}
/>
</Animated.View>
)
}
)
type Props = {
color?: TextStyle['color']
size?: number
featherProps?: Partial<Omit<React.ComponentProps<typeof Feather>, 'style'>>
fadeInDelay?: number
}
Hope it helps 👍
I am using animation for up and down in the react native, But the animation just slide from up to down and then stop at the bottom i want to move it up and down continuously. I have also used animation loop so please check and provide me solution for this
import React, { useEffect, useState } from 'react'
import { Text, View, Animated, Easing, StyleSheet } from 'react-native'
import LoaderLogo from '../../icons/commonicons/LoaderLogo'
import { Loadericon } from '../../constants/Image';
import LinearGradient from 'react-native-linear-gradient';
import { dynamicSize } from '../sizechoose';
const amimationScreen = () => {
const startValue = new Animated.Value(0);
const endValue = dynamicSize(225);
const startValue2 = new Animated.Value(225);
const endValue2 = dynamicSize(0);
const duration = 5000;
useEffect(() => {
Animated.sequence([
Animated.timing(startValue, {
toValue: endValue,
duration: duration,
useNativeDriver: true,
}),
Animated.timing(startValue2, {
toValue: endValue2,
duration: duration,
useNativeDriver: true,
})
]).start()
}, [startValue, endValue, duration]);
return (
<Animated.View style={[{ transform: [{ translateY: startValue }] }]}>
<View style={{backgroundColor:'red',height:10,width:100}}>
</View>
</Animated.View>
)
}
export default amimationScreen
I also tried with react-native-animatable package but it is not good to use for me as it starts animation from the top of the screen.
This worked for me:
const App = () => {
const animated = new Animated.Value(0);
const duration = 5000;
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(animated, {
toValue: 255,
duration: duration,
useNativeDriver: true,
}),
Animated.timing(animated, {
toValue: 0,
duration: duration,
useNativeDriver: true,
}),
]),
).start();
}, []);
return (
<Animated.View style={[{transform: [{translateY: animated}]}]}>
<View style={{backgroundColor: 'red', height: 10, width: 100}}></View>
</Animated.View>
);
};
So instead of having two instances of Animated.Value for translation, create one and let it transition from 0 to 255 and from 255 back to 0 in sequence. And make it loop once the sequence has finished.
I think the main problem in your original approach is that startValue decides how the view translates since this is what you pass as the value of translateY. The downward animation therefore happens correctly in your example. The upward animation however does not happen, because startValue2 is passed to Animated.timing and startValue is not used in the translation of any views in your example.
import React, { useEffect, useRef, useState } from 'react';
import { Animated, Dimensions, Easing, StyleSheet, View } from 'react-native';
export const App = () => {
const animatedValue = useRef(new Animated.Value(0)).current;
const [isTop, setIsTop] = useState(true);
const startAnimation = toValue => {
Animated.timing(animatedValue, {
toValue,
duration: 1000,
easing: Easing.linear,
useNativeDriver: true
}).start(() => {
setIsTop(!isTop);
})
}
useEffect(() => {
startAnimation(isTop ? 1 : 0);
}, [isTop]);
const translateY = animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, Dimensions.get('window').height - 70],
extrapolate: 'clamp'
})
return (
<View style={styles.container}>
<Animated.View style={[styles.square, { transform: [{ translateY }] }]}>
</Animated.View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center'
},
square: {
width: 70,
height: 70,
backgroundColor: 'red'
}
});
I'm learning React native and I would like to have heart beat animation
I did that but it's not the good result, I would like to have heart beat.
If someone can help me it's would be very nice thanks a lot
import React, { PureComponent } from "react";
import { Animated, StyleSheet, Text, View } from "react-native";
export class Loading extends PureComponent {
constructor(props: any) {
super(props);
this.state = {
opacity: new Animated.Value(0),
};
}
public componentDidMount() {
Animated.timing(
this.state.opacity,
{
toValue: 100,
duration: 5000,
},
).start();
}
public render() {
return (
<View>
<View>
<Animated.View
style={[styles.animation, {
opacity: this.state.opacity,
transform: [
{
scale: this.state.opacity.interpolate({
inputRange: [0.5, 1],
outputRange: [1, 0.95],
}),
}]},
]}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
animation: {
backgroundColor: "red,
width: 100,
height: 100,
borderRadius: 50,
},
});
A bit late but here is my own heartbeat animation made with React Native's Animated module:
export const HeartbeatAnimation = (
value: Animated.Value,
minValue: number,
maxValue: number
) =>
Animated.loop(
Animated.sequence([
Animated.timing(value, {
toValue: maxValue,
duration: 100
}),
Animated.timing(value, {
toValue: minValue,
duration: 100
}),
Animated.timing(value, {
toValue: maxValue,
duration: 100
}),
Animated.timing(value, {
toValue: minValue,
duration: 2000
})
])
);
Try playing with the minValue and maxValue to get your favorite animation !
As you say you are new to react native, I would like to suggest you use a react-native-animatable library which is very helpful with some built-in animation and custom animation.
Here is a link of GitHub https://github.com/oblador/react-native-animatable for your solution which I mentioned below.
In this page, you can find different methods for how to use animatable library for animation in react-native.
Now as per your question here is a solution
you have to install react-native-animatable by
$ npm install react-native-animatable --save
Step 1:
import * as Animatable from 'react-native-animatable';
Step 2: Use this code
<Animatable.Text
animation="pulse"
easing="ease-out"
iterationCount="infinite"
style={{ textAlign: 'center' }}>
❤️
</Animatable.Text>
you can use Animated.loop like this
useEffect(() => {
// makes the sequence loop
Animated.loop(
// runs given animations in a sequence
Animated.sequence([
// increase size
Animated.timing(anim.current, {
toValue: 10,
duration: 2000,
}),
// decrease size
Animated.timing(anim.current, {
toValue: 1,
duration: 2000,
}),
])
).start();
}, []);
Check below the full code and Live preview in Snack.
import React, { useRef, useEffect } from 'react';
import { Text, View, StyleSheet, Animated } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
export default function App() {
const anim = useRef(new Animated.Value(1));
useEffect(() => {
// makes the sequence loop
Animated.loop(
// runs given animations in a sequence
Animated.sequence([
// increase size
Animated.timing(anim.current, {
toValue: 10,
duration: 2000,
}),
// decrease size
Animated.timing(anim.current, {
toValue: 1,
duration: 2000,
}),
])
).start();
}, []);
return (
<View style={styles.container}>
<Animated.View style={{ transform: [{ scale: anim.current }] }}>
<Ionicons name="md-heart" size={32} color="red" />
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgb(254, 254, 254)',
padding: 8,
},
});
You can give React Native Lottie a go for a more flexible and appealing animation.
To get started, install it via:
Step 1: > npm i --save lottie-react-native#2.5.11
Step 2: > react-native link lottie-react-native
Step 3 : Go to Lottie Files, which is a collection of awesome animations done by the community. Search and choose a heartanimation that suits you and download the .jsonfile associated with it. Then proceed to render it as shown below:
import LottieView from 'lottie-react-native';
render() {
return (
<LottieView
ref={animation => {
this.animation = animation;
}}
source={require('../path/to/animation.json')}
/>
);
}
PS: I think This heart beat animation can fit your need. You can edit it's color and speed and then proceed to download it and use it in your app.
Use react-native-animatable:
<Animatable.Text
animation="pulse"
easing="ease-out"
iterationCount="infinite"
style={{ ... }}>❤️</Animatable.Text>
or...
<Animatable.View
animation="pulse"
easing="ease-out"
iterationCount="infinite"
style={{ ... }}>{children}</Animatable.View>
You can achieve that with react-native-animatable by creating your custom pulse animation:
const pulse = {
0: {
scale: 1,
},
0.5: {
scale: 1.5
},
1: {
scale: 1
}
}
Then, in your Animatable.View
<Animatable.View
animation={pulse}
easing="ease-out"
iterationCount="infinite"
>
<Text>PULSE ME</Text>
</Animatable.View>
I am attempting to create an infinite parallax background animation in React Native using a set of images. I have successfully created an animation. However, it seems like the longer the animations run, the more they seem unsynced.
Overall, I wrote code which creates three animations in this order:
Move the image component y-offset from its initial position to 0.
Move the image component y-offset from 0 to -image.height.
Move the image component y-offset instantly to the original sum of all image components.
Move the image component y-offset to 0 again.
Move the image component y-offset to -image.height again.
I put animation sequences 3-5 in a loop so they repeat indefinitely.
I also have the same issue without using Expo. I also thought about having the view position not being absolute so the views would be forced to touch each other. However, with that approach, I would have to re-render when I want to switch my component order.
I have created this Expo project to demonstrate what is happening.
Here is a screenshot of the symptom:
Here is my current code:
App.js
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Constants } from 'expo';
// You can import from local files
import ScrollingBackground from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<ScrollingBackground style={styles.scrollingBackground} images={[require('./assets/chess.png'),require('./assets/chess.png'),require('./assets/chess.png')]}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
},
scrollingBackground: {
width: '100%',
height: '100%',
backgroundColor: 'blue',
},
});
AssetExample.js
import React, { Component } from "react";
import {
StyleSheet,
View,
Animated,
Image,
Dimensions,
Easing
} from "react-native";
export default class ScrollingBackground extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
let imageComponents = [];
let lastImageYOffset = 0;
let counter = 0;
let deviceWidth = Dimensions.get("window").width;
this.props.images.forEach(image => {
const { width, height } = Image.resolveAssetSource(image);
let localElement = {};
let currentKey = "image" + counter.toString();
localElement.width = width;
localElement.height = (height * deviceWidth) / width;
localElement.initialOffset = lastImageYOffset;
localElement.topPositionAnimated = new Animated.Value(lastImageYOffset);
localElement.image = image;
localElement.currentKey = currentKey;
imageComponents.push(localElement);
lastImageYOffset = lastImageYOffset + localElement.height;
counter++;
});
lastImageYOffset = lastImageYOffset - imageComponents[imageComponents.length-1].height
this.setState({
imageComponents: imageComponents,
lastImageYOffset: lastImageYOffset
});
}
componentDidMount() {
let animations = [];
let arrayLength = this.state.imageComponents.length;
for (let i = 0; i < arrayLength; i++) {
// let height = -1 * this.state.imageComponents[i].height
// this.state.imageComponents[i].topPositionAnimated.addListener(({value}) => value == height ? console.log(this.state) : "");
animations.push(
Animated.sequence([
Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
toValue: 0,
duration:
10 *
(this.state.imageComponents[i].initialOffset),
delay: 0,
easing: Easing.linear,
useNativeDriver: true
}),
Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
toValue: -1 * this.state.imageComponents[i].height,
duration:
10 *
(this.state.imageComponents[i].height),
delay: 0,
easing: Easing.linear,
useNativeDriver: true
}),
Animated.loop(
Animated.sequence([
Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
toValue: this.state.lastImageYOffset,
duration: 0,
delay: 0,
useNativeDriver: true
}),
Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
toValue: 0,
duration:
10 *
(this.state.lastImageYOffset),
delay: 0,
easing: Easing.linear,
useNativeDriver: true
}),
Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
toValue: -1 * this.state.imageComponents[i].height,
duration:
10 *
(this.state.imageComponents[i].height),
delay: 0,
easing: Easing.linear,
useNativeDriver: true
}),
])
)
])
);
}
Animated.parallel(animations).start();
}
render() {
let elements = [];
for (imageComponent of this.state.imageComponents) {
elements.push(
<Animated.Image
key={imageComponent.currentKey}
source={imageComponent.image}
style={{
position: "absolute",
width: "100%",
height: imageComponent.height,
transform: [
{
translateY: imageComponent.topPositionAnimated
}
],
backgroundColor: "white"
}}
/>
);
}
return (
<View
style={[
styles.container,
{ backgroundColor: this.props.style.backgroundColor }
]}
>
{elements}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
width: "100%",
height: "100%"
}
});
Due to latency issues with animating multiple views, animating a container view containing all the individual image views turned out to be the better alternative. I ended up create an react native library for this: https://www.npmjs.com/package/react-native-scrolling-images
I'm working with the animation of react-native lately and I'm trying to make a View component moving from side to side by click.
This is the code I have so far that works :
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Animated,
Easing,
TouchableOpacity,
Dimensions
} from 'react-native';
export default class App extends Component {
constructor () {
super()
this.state = {
isLeftSide: true,
}
this.animatedValue = new Animated.Value(0)
}
animate () {
this.animatedValue.setValue(0);
Animated.timing(
this.animatedValue,
{
toValue: 1,
duration: 300,
easing: Easing.linear
}
).start()
}
fire = () => {
this.animate();
this.setState({isLeftSide: (!this.state.isLeftSide)});
}
direction = () => this.state.isLeftSide ? 'rtl' : 'ltr';
render() {
const screenWidth = Dimensions.get('screen').width;
const objectMaxCoord = screenWidth - 40;
const outputRange = {
rtl: [0, objectMaxCoord],
ltr: [objectMaxCoord, 0]
}
const marginLeft = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: outputRange[this.direction()]
})
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => this.fire()}><Text>Run</Text></TouchableOpacity>
<Animated.View
style={{
marginLeft,
height: 30,
width: 40,
backgroundColor: 'red'}} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
marginTop: 30
}
});
Now, the problem is that the red cube starts on the left side, but as I click run, it jumps (without animation) to the right corner and then move smoothly(in animation) to the left side. what comes after that works just perfect. Why is this first jump happens ?
And is there any easier way to make this animation done ?
( P.S I'm working on android )
Here is a link to an expo
https://snack.expo.io/S1aAgNq6Z
Probably because the state might not update right away. Try to change your fire function like this:
this.setState({isLeftSide: (!this.state.isLeftSide)}, () => {
this.animate();
});
setState accepts a callback when done.
I was playing with a Snack for a while and the issue is clearly that the state gets unsync because it says is on the left side when actually is on the right. I don't see anything else messing with the state.
You can see it here: https://snack.expo.io/BJhb4-pAW
The snack is somehow limited so can you try this on your code?
finishedAnimation = (finished) => {
if (finished)
this.setState({isLeftSide: !(this.state.isLeftSide)});
}
fire = () => {
this.animatedValue.setValue(0);
Animated.timing(
this.animatedValue,
{
toValue: 1,
duration: 300,
easing: Easing.linear
}
).start(this.finishedAnimation);
}