I have a screen with four animated Views. They are rendered by a map function. When the screen is mounted a scale animation is triggered for once. After that, i'm trying to achieve to randomly animate (scale simple animation) only one View every 4 seconds. I can't find the way to get the reference of each View and only animate one randomly. Here is my code:
import { useRef, useEffect } from 'react'
import { View, StyleSheet, TouchableOpacity } from 'react-native'
import Animated, { useAnimatedStyle, useSharedValue, withTiming, withSpring, withRepeat } from 'react-native-reanimated'
const SIZE = 65.0
const RandomZoomInScreen = ({ navigation }) => {
const scale = useSharedValue(0)
const bounce = useSharedValue(1)
const itemEls = useRef(new Array())
const reanimatedStyle = useAnimatedStyle(() => {
return {
transform: [{ scale: scale.value }]
}
})
const bounceStyle = useAnimatedStyle(() => {
return {
transform: [{ scale: bounce.value }]
}
})
useEffect(() => {
scale.value = withSpring(1, {stiffness:200})
return () => {
scale.value = 0
}
}, [])
useEffect(() => {
const intervalId = setInterval(() => { //assign interval to a variable to clear it.
'worklet'
bounce.value = withRepeat(withTiming(1.3),2,true)
}, 4000)
return () => clearInterval(intervalId)
}, [])
return (
<View style={styles.container}>
{[1, 2, 3, 4].map((square, index) => {
return <TouchableOpacity
activeOpacity={0.7}
key={index}
>
<Animated.View
ref={(element) => itemEls.current.push(element)} style={[styles.square, reanimatedStyle, bounceStyle]} />
</TouchableOpacity>
})}
</View>
)
}
export default RandomZoomInScreen
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-evenly',
alignItems: 'center'
},
square: {
width: SIZE * 2,
height: SIZE,
backgroundColor: 'green',
}
})
CodePudding user response:
You want to animate a single box every 4 seconds, right?
If you want to animate a single box, I think the best way is to create individual shared values for each box, by either creating a Box component, or creating an array of shared values (or something like that), so that you can change each value independently.
Here I refactored your code, creating a Box component, and creating a scale shared value inside the component:
import { useEffect, useState } from "react";
import { View, StyleSheet, TouchableOpacity } from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
withSequence,
withTiming,
} from "react-native-reanimated";
const SIZE = 65.0;
const Box = ({ shouldAnimate }) => {
const scale = useSharedValue(0);
const reanimatedStyle = useAnimatedStyle(() => {
return {
transform: [{ scale: scale.value }],
};
});
useEffect(() => {
scale.value = withSpring(1, { stiffness: 200 });
return () => {
scale.value = 0;
};
}, []);
useEffect(() => {
if (shouldAnimate) {
scale.value = withSequence(withTiming(1.3), withTiming(1));
}
}, [shouldAnimate]);
return (
<TouchableOpacity activeOpacity={0.7}>
<Animated.View style={[styles.square, reanimatedStyle]} />
</TouchableOpacity>
);
};
const RandomZoomInScreen = ({ navigation }) => {
const [selectedBox, setSelectedBox] = useState(-1);
useEffect(() => {
const intervalId = setInterval(() => {
// you want to select a number between 0 and 3 (the indeces of the boxes) every 4 seconds
const nextBox = Math.floor(Math.random() * 4);
// to garantee the new value will always be different from previous one...
// we can sum 1 and apply a mod 4 (so the result is always between 0 and 3)
setSelectedBox((previousBox) =>
previousBox === nextBox ? (nextBox 1) % 4 : nextBox
);
}, 4000);
return () => clearInterval(intervalId);
}, []);
return (
<View style={styles.container}>
{[1, 2, 3, 4].map((square, index) => {
// we should animate when the selected box is equal to the index
return <Box key={square} shouldAnimate={index === selectedBox} />;
})}
</View>
);
};
export default RandomZoomInScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "space-evenly",
alignItems: "center",
},
square: {
width: SIZE * 2,
height: SIZE,
backgroundColor: "green",
},
});
Keep in mind that there are probably other approaches to achieving this.