Restart React Native Animation When Component's Props Change - react-native

I have a React Native component rendering a notification text. I want the text to fade in and fade out after a small delay. When a new notification text is set, I want to restart the same animation.
I have the following code:
export default function Notification({ notification }) {
if (!notification) {
return null;
}
const [fadeAnimation] = useState(new Animated.Value(0));
useEffect(() => {
Animated.sequence([
Animated.timing(fadeAnimation, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}),
Animated.delay(3000),
Animated.timing(fadeAnimation, {
toValue: 0,
duration: 500,
useNativeDriver: true,
})]).start()
}, []);
return (
<Animated.View style={{ opacity: fadeAnimation, }}>
<View style={styles.notificaton}>
<Text style={styles.text}>{notification}</Text>
</View>
</Animated.View >
)
}
I read that I should be able to reset the animation with setValue(0) again, however I do not know where and when to call it.

work for me
import React, { Component } from 'react';
import { Animated, View } from 'react-native';
class Testing extends Component {
constructor(props) {
super(props);
this.animatedValue = new Animated.Value(0);
}
componentDidUpdate(prevProps) {
if (prevProps.myProp !== this.props.myProp) {
this.startAnimation();
}
}
startAnimation() {
this.animatedValue.setValue(0);
Animated.timing(this.animatedValue, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}
render() {
const { myProp } = this.props;
const opacity = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
});
return (
<View>
<Animated.Text style={{ opacity }}>
My prop val : {myProp}
</Animated.Text>
</View>
);
}
}
export default Testing;

Related

Slide up down animation in the react native

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'
}
});

In react-native, how can I make a View's background color change to another color, and then fade out?

I'd like the View to change to dark blue, and then slowly fade back to normal (aka white).
How can this be done?
You can use animated by react-native.
Here's a sample code to achieve what you are looking for
import * as React from "react";
import { Text, View, StyleSheet, Animated } from "react-native";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
colorAnimation: new Animated.Value(0)
};
}
componentDidMount() {
const { colorAnimation } = this.state;
{
/* Change Color To blue */
}
Animated.timing(colorAnimation, {
toValue: 255,
duration: 1000 //Animation Duration
}).start();
{
/* After 1 second change color back to white */
}
setInterval(() => {
Animated.timing(colorAnimation, {
toValue: 0,
duration: 3000 //Animation Duration
}).start();
}, 1000);
}
render() {
const interpolatedColor = this.state.colorAnimation.interpolate({
inputRange: [0, 255],
outputRange: ["rgb(255,255,255)", "rgb(0, 0, 139)"]
});
return (
<Animated.View
style={[styles.container, { backgroundColor: interpolatedColor }]}
></Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 8
}
});
You can view the demo here.

React Native - Animation works in iOS and Android Emulator, but not in Android Device

This is an extremely unusual problem...
I have a View that acts like a flashcard. I have used animations in order to flip over the flashcard.
After I click the flashcard, the system completely breaks down. It works fine ONCE, but then it doesn't even detect the clicks.
import React, {Component} from 'react';
import { View, Text, StyleSheet, Animated, TouchableWithoutFeedback, Easing } from 'react-native';
import Swiper from 'react-native-swiper';
import AppText from "../components/AppText";
type Props = {};
export default class FlashcardScreen extends Component<Props> {
static navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam('title', 'Flashcards')
}
};
constructor(props){
super(props);
this.animatedValue = new Animated.Value(0);
this.value = 0;
this.textAnimatedValue = new Animated.Value(0);
this.animatedValue.addListener(({value}) => {
this.value = value;
if (value === 90){
Animated.timing(this.textAnimatedValue, {
toValue: 0,
duration: 250,
}).start();
}
});
}
state = {
cards: this.props.navigation.state.params.cards,
displayTexts: Object.keys(this.props.navigation.state.params.cards),
index: 0,
};
flipAnimation = (index) => {
alert("Clicked!");
let { displayTexts, cards } = this.state;
const tempDisplayTexts = [...displayTexts];
const toValue = this.value >= 90 ? 0 : 180;
const entry = Object.entries(cards)[index];
Animated.parallel([
Animated.timing(this.animatedValue, {
toValue: 90,
duration: 250,
easing: Easing.linear
}),
Animated.timing(this.textAnimatedValue, {
toValue: 90,
duration: 250
})
]).start(() => {
if(displayTexts[index] === entry[0]){
tempDisplayTexts[index] = entry[1];
} else {
tempDisplayTexts[index] = entry[0];
}
this.setState({ displayTexts: tempDisplayTexts });
Animated.parallel([
Animated.timing(this.animatedValue, {
toValue: toValue,
duration: 250,
easing: Easing.linear
}),
]).start();
});
};
render() {
let { cards, displayTexts } = this.state;
this.SetInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg'],
});
const Rotate_X_AnimatedStyle = {
transform: [{ rotateX: this.SetInterpolate }],
};
this.textSetInterpolate = this.textAnimatedValue.interpolate({
inputRange: [0, 90],
outputRange: ['0deg', '90deg'],
});
const Test_Rotate_X_AnimatedStyle = {
transform: [{ rotateX: this.textSetInterpolate }]
};
return (
<View style={styles.container}>
<Swiper showsPagination={false} loop={false} onIndexChanged={(index) => this.setState({ index })}>
{
Object.entries(cards).map((question, index) => {
return (
<View style={styles.main}>
<TouchableWithoutFeedback onPress={() => this.flipAnimation(index)}>
<Animated.View style={[Rotate_X_AnimatedStyle, styles.card]} />
</TouchableWithoutFeedback>
<Animated.Text
onPress={() => this.flipAnimation(index)}
style={[styles.text, Test_Rotate_X_AnimatedStyle]}>
{displayTexts[index]}
</Animated.Text>
</View>
)
})
}
</Swiper>
<AppText style={styles.position} text={(this.state.index + 1) + " / " + this.state.displayTexts.length} />
</View>
);
}
}
I tried to change the code so that each card has its own animatedValue and textValue, but it did not fix the problem.
Here is a link to my recordings. The first one is the emulator, while the second one is the device:
https://imgur.com/gallery/xKYs3bc
Thanks in advance for any help! I've spent hours on this :/

React native animation of a View from side to side

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);
}

How to change Text in Animation in react-native?

I use Animated.Text for change Animation Text but it's not working properly
I also require in animation fade out old text & fade in the new text.
import React, { Component, PropTypes } from 'react';
import {
StyleSheet,
View,
Text,
Image,
Dimensions,
Animated
} from 'react-native';
import styles from './styles';
const win = Dimensions.get('window');
export default class Logo extends Component {
constructor(props) {
super(props);
this.tempText = new Animated.Value("Hello");
}
componentWillMount () {
Animated.timing(this.tempText, {
duration: 5000,
toValue: "New Text",
}).start();
};
render() {
return (
<View style={{flex:1}}>
<Animated.Text style={{color: "#9b9b9b"}}>
{this.tempText}
</Animated.Text>
</View>
);
}
}
Actual output Get - Change text after 5 Second but it's not working.please help me.
What you're trying to achieve can be done without using Animated at all, and actually, Animated isn't intended for this particular use.
Replacing the text can be done with a simple variable, and the text change can be triggered by a setTimeout.
Animated is intended for changing a numeric value, not a text value. Think of it this way - if the change is supposed to happen over a 5 second interval, what would the mid-value be?
Do this instead:
export default class Logo extends Component {
constructor(props) {
super(props);
this.state = {text: "Hello"};
}
componentDidMount () {
setTimeout(() => this.setState({text: "New Text"}), 5000);
};
render() {
return (
<View style={{flex:1}}>
<Animated.Text style={{color: "#9b9b9b"}}>
{this.state.text}
</Animated.Text>
</View>
);
}
}
My example with smoothly opacity animation.
Sorry, without fadeIn, fadeOut.
const inputRange = [0, 1, 2, 3];
const AnimatedText = Animated.createAnimatedComponent(Text);
const animationProps = {
duration: 500,
easing: Easing.out(Easing.linear),
isInteraction: false,
useNativeDriver: true,
};
class Onboarding extends PureComponent {
activeDot = 0;
animationDot = new Animated.Value(0);
toggleOnButton = () => {
Animated.timing(this.animationDot, {
toValue: this.activeDot + 1,
...animationProps,
}).start((endState) => {
if (endState.finished) {
this.activeDot = this.activeDot + 1;
}
});
}
renderButton = () => {
const opacityNext = this.animationDot.interpolate({
inputRange,
outputRange: [1, 1, 1, 0]
});
const opacityGetStarted = this.animationDot.interpolate({
inputRange,
outputRange: [0, 0, 0, 1]
});
return (
<TouchableOpacity style={styles.button} onPress={this.toggleOnButton}>
<AnimatedText style={[styles.buttonText, { opacity: opacityNext }]}>
Next
</AnimatedText>
<AnimatedText style={[styles.buttonText, {
top: normalize(isIOS ? 12 : 8), position: 'absolute', opacity: opacityGetStarted
}]}
>
Get started
</AnimatedText>
</TouchableOpacity>
);
}
}