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'
}
});
Related
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;
I am trying to change a class into a function. This is mainly because if I can get it working, I want to use it for learning different animations, I have had some success but not 100%. Originally it displayed an icon that when clicked it spun it one way and then when clicked again it spun the other way. What I have tried to do it get rid of the icon and replace it with an image. It works when clicked once but then does nothing.
I am struggling with toggled aspect of it and setting the state I think because I cant seem to set it up properly in a function.
I have tried several things but this is the best I can get. If I show the original code and then what I have managed to change, maybe someone can point me in the right direction as to what I am doing wrong.
All I want is the image to display and then when clicked spins right and then if clicked again it spins left.
I am doing this so I can mess around with the settings and hopefully learn animation a bit better.
Any help would be greatly appreciated.
The original code :
import React from 'react';
import { View, StyleSheet, Animated, Image, TouchableOpacity } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
const TabIcon = ({
onPress,
menuToggled
}) => {
const logoStyles = [styles.logoStyle];
if (menuToggled !== null) {
const animation = new Animated.Value(menuToggled ? 0 : 1);
Animated.timing(animation, {
toValue: menuToggled ? 1 : 0,
duration: 500,
useNativeDriver: true
}).start();
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
}
return (
<TouchableOpacity
style={styles.tabStyle}
onPress={onPress}
>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/></Animated.View>
</TouchableOpacity>
);
};
export default class App extends React.Component {
state = {
menuToggled: null
}
toggleMenu = () => {
this.setState(prevState => {
return { menuToggled: !prevState.menuToggled };
});
}
render () {
return (
<View style={styles.container}>
<TabIcon
onPress={this.toggleMenu}
menuToggled={this.state.menuToggled}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
and what I have changed so far :
import React, { useRef, useState } from "react";
import { View, StyleSheet, Animated, Image, TouchableOpacity, Easing } from 'react-native';
import Constants from 'expo-constants';
const App = () => {
const spinValue = useRef(new Animated.Value(0)).current;
const [menuToggled, setMenuToggled] = useState([null]);
toggleMenu = () => {
setMenuToggled(menuToggled === "null" ? "menuToggled" : "null");
}
const Spinner = ({
onPress,
menuToggled
}) => {
const logoStyles = [styles.logoStyle];
const animation = new Animated.Value(0);
const go = () => {
Animated.timing(animation, {
toValue: 1,
duration: 1500,
easing: Easing.elastic(1),
useNativeDriver: true
}).start();
}
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
return (
<TouchableOpacity
onPress={go}
>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/></Animated.View>
</TouchableOpacity>
);
};
return (
<View style={styles.container}>
<Spinner
onPress={toggleMenu}
menuToggled={menuToggled}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
export default App;
There are a few issues. You first had menuToggled initialized to [null] when it should have been null. You also had forgotten to use onPress in TabIcon. The most noteworthy thing was wrapping TabIcon in a useCallback to prevent it from being recreated all the time. Expo snack:
import React, { useRef, useState, useCallback } from 'react';
import {
View,
StyleSheet,
Animated,
Image,
TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';
const App = () => {
const spinValue = useRef(new Animated.Value(0)).current;
const [menuToggled, setMenuToggled] = useState(null);
const TabIcon = useCallback(({ onPress, menuToggled }) => {
const logoStyles = [styles.logoStyle];
// initialized base on menuToggled
// if not done then it will take an additional button press to trigger
// the animation
const animation = useRef(new Animated.Value(menuToggled ? 0 : 1)).current;
const startAnimation = () => {
Animated.timing(animation, {
toValue: menuToggled ? 1 :0,
duration: 500,
useNativeDriver: true,
}).start();
};
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
return (
<TouchableOpacity
onPress={() => {
startAnimation();
onPress?.();
}}>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/>
</Animated.View>
</TouchableOpacity>
);
},[]);
return (
<View style={styles.container}>
<TabIcon
onPress={() => setMenuToggled((prev) => !prev)}
menuToggled={menuToggled}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
export default App;
I'm trying to create a button that resizes (gets a little bit smaller when it' pressed). I use TouchableWithoutFeedback from react-native-gesture-handlerand I use react-native-reanimated.
This is my code so far:
import React, { useState } from 'react';
import { View } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
const { interpolate, sub } = Animated;
const TouchableResize = (props) => {
const { onPress, children } = props;
const [scale, setScale] = useState(0);
const scaling = interpolate(scale, {
inputRange: [0, 1],
outputRange: [1, 0.90],
extrapolate: Extrapolate.CLAMP
});
return (
<TouchableWithoutFeedback onPressIn={() => setScale(1)} onPressOut={() => setScale(0)}>
<Animated.View style={{ transform: [{ scaleX: scaling }, { scaleY: scaling }] }}>
{children}
</Animated.View>
</TouchableWithoutFeedback>
);
};
export { TouchableResize };
This code works partly. The button resizes to 0.90 when it's pressed, but the animation is not smooth. It snaps directly to 0.90, and when it's released, the button directly snaps back.
How can I update my code so the animation runs smoothly? Please note I'm a complete beginner in react-native-reanimated.
You have to use timing function to change your Animated.Value over time. Here example in docs. Also, I created expo snack example. Here updated component code
import React, { useState, useMemo } from 'react';
import { View } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
const {
Clock,
Value,
set,
cond,
startClock,
clockRunning,
timing,
debug,
stopClock,
block,
interpolate,
useCode,
} = Animated;
function runTiming(clock, from, to) {
const state = {
finished: new Value(0),
position: new Value(from),
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration: 100,
toValue: new Value(to),
easing: Easing.inOut(Easing.ease),
};
return block([
cond(
clockRunning(clock),
[],
startClock(clock),
),
// we run the step here that is going to update position
timing(clock, state, config),
// if the animation is over we stop the clock
cond(state.finished, debug('stop clock', stopClock(clock))),
// we made the block return the updated position
state.position,
]);
}
const TouchableResize = (props) => {
const { onPress, children } = props;
const [pressed, setPressed] = useState(false);
const {clock, scale} = useMemo(() => ({
clock: new Clock(),
scale: new Value(1),
}), [])
useCode(
() => block([
pressed ? set(scale, runTiming(clock, 0, 1)) : set(scale, runTiming(clock, 1, 0))
]), [pressed]
);
const scaling = interpolate(scale, {
inputRange: [0, 1],
outputRange: [1, 0.90],
extrapolate: Extrapolate.CLAMP
});
return (
<TouchableWithoutFeedback onPressIn={() => setPressed(true)} onPressOut={() => setPressed(false)}>
<Animated.View style={{ transform: [{ scaleX: scaling }, { scaleY: scaling }] }}>
{children}
</Animated.View>
</TouchableWithoutFeedback>
);
};
export { TouchableResize };
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 :/
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);
}