I have that code :
export default function Shimmer() {
const AnimatedView = Animated.createAnimatedComponent(LinearGradient);
const animatedValue = new Animated.Value(0);
const translateX = animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [-width, width],
});
useEffect(() => {
Animated.loop(
Animated.timing(
(animatedValue,
{
toValue: 1,
duration: 1000,
easing: Easing.linear,
useNativeDriver: true,
})
)
).start();
});
return (
<View style={styles.container}>
<View style={styles.backgroundView}>
<AnimatedView
colors={["#a0a0a0", "#b0b0b0", "#b0b0b0", "#a0a0a0"]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={[
StyleSheet.absoluteFillObject,
{
transform: [{ translateX: translateX }],
},
]}
/>
</View>
</View>
);
}
I would like to make a shimmer effect like shown in this video :
https://www.youtube.com/watch?v=PPwa0WMAH8A
I have set useNativeDriver to true, but I get this error :
Cannot read properties of undefined (reading 'useNativeDriver')
How can I fix that (I am working with Expo) ?
Problem solved by removing a parenthesis here :
Animated.loop(
Animated.timing(
animatedValue,
{
toValue: 1,
duration: 1000,
easing: Easing.linear,
useNativeDriver: true,
}
)
).start();
});
I made a custom TextInput that I'm animating into view on my Registration screen component. The issue is that for every keystroke onChangeText, the animation plays again and this is not the expected behavior. If I take out the animation, the TextInput works fine.
I've tried changing my dependency on my useState, also tried to wrap the component with a useCallback and useMemo and none has gotten it to work.
Also worthy of note is that I'm handling my state management with useReducer. Code below
const RegistrationScreen = ({ navigation }: any) => {
const textPosition = new Animated.Value(width);
const inputPosition = new Animated.Value(width);
const inputPosition2 = new Animated.Value(width);
const inputPosition3 = new Animated.Value(width);
const inputPosition4 = new Animated.Value(width);
const [state, dispatch] = useReducer(reducer, initialState);
const { name, email_address, phone_number, password } = state;
const animate = Animated.sequence([
Animated.parallel([
Animated.timing(textPosition, {
toValue: 0,
delay: 50,
duration: 1000,
useNativeDriver: true,
easing: Easing.elastic(3),
}),
Animated.timing(inputPosition, {
toValue: 0,
delay: 100,
duration: 1000,
useNativeDriver: true,
easing: Easing.elastic(3),
}),
Animated.timing(inputPosition2, {
toValue: 0,
delay: 200,
duration: 1000,
useNativeDriver: true,
easing: Easing.elastic(3),
}),
Animated.timing(inputPosition3, {
toValue: 0,
delay: 300,
duration: 1000,
useNativeDriver: true,
easing: Easing.elastic(3),
}),
Animated.timing(inputPosition4, {
toValue: 0,
delay: 400,
duration: 1000,
useNativeDriver: true,
easing: Easing.elastic(3),
}),
]),
]);
const _onCreateAccountHandler = () => dispatch(getPhoneVerificationCode(phone_number));
const _onChangeHandler = (field: any, value: any) => dispatch({ type: 'FIELD', field, value });
useEffect(() => {
animate.start();
}, [animate]);
return (
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAvoidingView
style={{ flex: 1 }}
keyboardVerticalOffset={100}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.container}>
<View style={{ flex: 1 }}>
<Header
backButtonEnabled
backButtonColor={colors.darkGray}
onBackButtonPress={() => navigation.goBack(null)}
/>
</View>
<View style={styles.innerContainer}>
<Animated.View
style={[
styles.createAccount,
{
transform: [
{
translateX: textPosition,
},
],
},
]}>
<Text style={styles.creaetAccountText}>{strings.create_an_account}</Text>
</Animated.View>
<View style={styles.textAreaContainer}>
<Animated.View
style={[
styles.textInputContainer,
{
transform: [
{
translateX: inputPosition,
},
],
},
]}>
<TextInput
placeHolder={strings.name}
value={name}
onChangeText={(text: any) => _onChangeHandler('name', text)}
onCancelPressed={() => {}}
placeHolderStyle={{
backgroundColor: colors.lightWhite,
}}
autoCorrect={false}
/>
</Animated.View>
You could try wrapping animate with React.useCallback()
So From further research, it turns out that since I'm animating the component with a value that is always constant (width), onTextChange will always rerender the screen and any value that is different will be accounted for, hence, the animation plays again since the current value is different from the initial value.
My Solution:
I had to use useState to change the initial value as soon as the first animation completes. I also had to call that in a setTimeout that runs after the animation that way - the initial width will update to become the current width and when the component re-renders, it won't render again. (or in this case, there's no difference in the translate X value, so animation won't play again)
I want to shake the below View when my password is wrong.
for example:
it should be translateX from place 10, to place 20 for 4 time and 1 second.
then should be stopped in place 10.
place 10 (I mean the X position of View)
startShake = () => {
Animated.loop(
Animated.sequence([
Animated.timing(this.animatedValue, {toValue: 1, duration: 150, easing: Easing.linear, useNativeDriver: true}),
Animated.timing(this.animatedValue, {toValue: -1, duration: 300, easing: Easing.linear, useNativeDriver: true}),
Animated.timing(this.animatedValue, {toValue: 0, duration: 150, easing: Easing.linear, useNativeDriver: true}),
])
).start();
}
<Animated.View style={{transform: [{
translateX: this.animatedValue.interpolate({
inputRange: [0, 0],
outputRange: [0, 0]
})
}]
}}>
</Animated.View>
Thank you for all answers.
I just solved editing my code with the following code
constructor(props) {
super(props)
this.shakeAnimation = new Animated.Value(0);
}
startShake = () => {
Animated.sequence([
Animated.timing(this.shakeAnimation, { toValue: 10, duration: 100, useNativeDriver: true }),
Animated.timing(this.shakeAnimation, { toValue: -10, duration: 100, useNativeDriver: true }),
Animated.timing(this.shakeAnimation, { toValue: 10, duration: 100, useNativeDriver: true }),
Animated.timing(this.shakeAnimation, { toValue: 0, duration: 100, useNativeDriver: true })
]).start();
}
<Animated.View style={{ transform: [{translateX: this.shakeAnimation}] }}>
</Animated.View>
Here is the shake animation for Image component in react native, you can check it-
const bgImage = require('./components/images/ex.jpg')
class App extends Component {
constructor(props) {
super(props)
this.animatedValue = new Animated.Value(0)
}
handleAnimation = () => {
// A loop is needed for continuous animation
Animated.loop(
// Animation consists of a sequence of steps
Animated.sequence([
// start rotation in one direction (only half the time is needed)
Animated.timing(this.animatedValue, {toValue: 1.0, duration: 150, easing: Easing.linear, useNativeDriver: true}),
// rotate in other direction, to minimum value (= twice the duration of above)
Animated.timing(this.animatedValue, {toValue: -1.0, duration: 300, easing: Easing.linear, useNativeDriver: true}),
// return to begin position
Animated.timing(this.animatedValue, {toValue: 0.0, duration: 150, easing: Easing.linear, useNativeDriver: true})
])
).start();
}
}
To add this Animation to Image Component-
<Animated.Image
source={bgImage}
resizeMode='contain'
style={{
transform: [{
rotate: this.animatedValue.interpolate({
inputRange: [-1, 1],
outputRange: ['-0.1rad', '0.1rad']
})
}]
}}
/>
There is a library : react-native-animitable
You can do wonders using this library and is really very easy to use with least codes.
You can do this without loop.
startShake = () => {
this.animatedValue.setValue(0);
Animated.timing(this.animatedValue,
{
toValue: 1,
duration: 150,
easing: Easing.linear,
useNativeDriver: true
}
).start()
}
<Animated.View style={{transform: [{
translateX: this.animatedValue.interpolate({
inputRange: [0, 0.25, 0.50, 0.75, 1],
outputRange: [10, 20, 10, 20, 10]
})
}]
}}>
</Animated.View>
It might some help you to get your required animation
class App extends Component {
constructor(props) {
super(props)
this.animatedValue = new Animated.Value(0)
}
handleAnimation = () => {
// A loop is needed for continuous animation
Animated.loop(
// Animation consists of a sequence of steps
Animated.sequence([
// start rotation in one direction (only half the time is needed)
Animated.timing(this.animatedValue, {toValue: 1.0, duration: 150, easing: Easing.linear, useNativeDriver: true}),
// rotate in other direction, to minimum value (= twice the duration of above)
Animated.timing(this.animatedValue, {toValue: -1.0, duration: 300, easing: Easing.linear, useNativeDriver: true}),
// return to begin position
Animated.timing(this.animatedValue, {toValue: 0.0, duration: 150, easing: Easing.linear, useNativeDriver: true})
])
).start();
}
}
<Animated.View
style={{
transform: [{
rotate: this.animatedValue.interpolate({
inputRange: [-1, 1],
outputRange: ['-0.1rad', '0.1rad']
})
}]
}}
/>
It can be a good solution with two axes:
import React, { useState } from "react";
import { StyleSheet, View, Animated, TouchableWithoutFeedback, Easing } from "react-native";
const Shake = () => {
const [animation] = useState(new Animated.Value(0));
const startAnimation = () => {
animation.setValue(0);
Animated.timing(animation, {
toValue: 1,
duration: 1500,
easing: Easing.linear,
useNativeDriver: true
}).start(()=>{
animation.setValue(0)
});
}
const animatedStyles = {
transform: [
{
translateX: animation.interpolate({
inputRange: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
outputRange: [0, 10, -10, 10, -10, 0, 0, 0, 0, 0, 0]
})
},
{
translateY: animation.interpolate({
inputRange: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
outputRange: [0, 0, 0, 0, 0, 0, 10, -10, 10, -10, 0]
})
}
]
}
return <View style={styles.container}>
<TouchableWithoutFeedback onPress={startAnimation}>
<Animated.View style={[styles.box, animatedStyles]} />
</TouchableWithoutFeedback>
</View>
}
const styles = StyleSheet.create({
container: {
flex: 1,
height: 600,
alignItems: "center",
justifyContent: "center",
},
box: {
backgroundColor: "tomato",
width:150,
height:150
}
});
export default Shake
// #flow
import React, { useCallback, useEffect, useRef } from 'react'
import { Image, Animated } from 'react-native'
// Constants
const DEFAULT_STEP_TIME = 1000
interface Props {
source: string | number;
intensity?: number;
stepTime?: number;
iterations?: number;
containerStyle?: any;
style?: any;
}
const ShakingIcon = ({
source,
intensity = 2,
stepTime,
iterations = 2,
containerStyle,
style,
}: Props) => {
const interval = useRef(null)
const animation = useRef(new Animated.Value(0))
const shakeAnimation = useCallback(() => {
Animated.loop(
Animated.sequence([
Animated.timing(animation.current, {
toValue: -intensity,
duration: 50,
useNativeDriver: true,
}),
Animated.timing(animation.current, {
toValue: intensity,
duration: 50,
useNativeDriver: true,
}),
Animated.timing(animation.current, {
toValue: 0,
duration: 50,
useNativeDriver: true,
}),
]),
{ iterations },
).start()
}, [])
const runAnimation = useCallback(async () => {
interval.current = setInterval(
() => shakeAnimation(),
stepTime || DEFAULT_STEP_TIME,
)
}, [])
useEffect(() => {
runAnimation()
return () => clearInterval(interval.current)
}, [])
return (
<Animated.View
style={[
containerStyle,
{ transform: [{ translateX: animation.current }] },
]}
>
<Image source={source} style={style} />
</Animated.View>
)
}
export default ShakingIcon
based on https://snack.expo.dev/embedded/#ananthu.kanive/shake?iframeId=t099o2svbu&preview=true&platform=web&theme=light
import React,{Component} from 'react';
import {Text,View,Button,Animated,Easing,Image} from 'react-native';
class ToggleText extends Component{
constructor(props){
super(props);
this.spinValue = new Animated.Value(0);
}
spin () {
this.spinValue.setValue(0)
Animated.timing(
this.spinValue,
{
toValue: 1,
duration: 4000,
easing: Easing.linear,
}
).start(() => this.spin())
}
componentDidMount () {
this.spin();
}
render(){
const spin = this.spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
})
return(
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Animated.Image
style={{
width: 227,
height: 200,
transform: [{rotate: spin}] }}
source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png'}}
/>
</View>
);
}
}
export default ToggleText;
I also have similar issue in Android where the animation stuck. I found that it is just lagging.
I solved it by using native driver. By adding this useNativeDriver: true,
Animated.timing(
this.spinValue,
{
toValue: 1,
duration: 4000,
easing: Easing.linear,
useNativeDriver: true, //<---Add this
}
I have the following animation.
componentWillMount(){
this.animatedValue = new Animated.Value(300);
}
componentDidMount(){
Animated.timing( this.animatedValue , {
toValue: -100,
duration: 3000,
} ).start();
}
render() {
const animatedStyle = { marginLeft: this.animatedValue, marginRight: - this.animatedValue };
return (
<View style={{flexDirection: 'row', height: 100,}}>
<Animated.View style={[ animatedStyle,{backgroundColor: 'blue', width:100}]} />
</View>
);
}
I would like to repeat endless times. Anyone have any suggestions?
2019 solution:
Animated.loop(Animated.timing(this.animatedValue , {
toValue: -100,
duration: 3000,
})).start();
Pass a callback to Animated.start() that resets the Animated value and starts the animation again. For example:
componentDidMount(){
this.runAnimation();
}
runAnimation() {
this.animatedValue.setValue(300);
Animated.timing(this.animatedValue, {
toValue: -100,
duration: 3000,
}).start(() => this.runAnimation());
}
If you need to stop the animation at any point, take a look at this question/answer.