I have a view full of red circles.
I would like to animate their size/borderRadius based on which circle is highlighted (biggerCircleIndex)
The code looks like this now:
import React from 'react';
const Circles = props => {
const myCircles = [0, 1, 2, 3, 4, 5];
const [biggerCircleIndex, setBiggerCircleIndex] = useState(2);
return (
<View>
{myCircles.map((index) => {
return (
<Animated.View
key={index}
style={[
{backgroundColor: 'red'},
(biggerCircleIndex == index)
? {width: 100, height: 100, borderRadius: 50}
: {width: 50, height: 50, borderRadius: 25},
]}>
</Animated.View>
);
})}
</View>
);
};
export {Circles};
Tried using animation for the size but failed: the circles aren't animated when I update the biggerCircleIndex
I think I should create an array of animations, but I really don't know how.
Can someone help me out?
Here's the code snippet how I've been trying:
import React from 'react';
const Circles = props => {
const myCircles = [0, 1, 2, 3, 4, 5];
const [biggerCircleIndex, setBiggerCircleIndex] = useState(2);
const inputRange = [0, 100];
const outputRange = [50, 100];
const animation = React.useRef(new Animated.Value(0)).current
const animatedSize = animation.interpolate({inputRange, outputRange});
const animationReverse = React.useRef(new Animated.Value(100)).current
const animatedSizeReverse = animationReverse.interpolate({inputRange, outputRange});
React.useEffect(() => {
Animated.timing(
animation,
{
toValue: 100,
duration: 500,
useNativeDriver: false,
}
).start();
}, [animation])
React.useEffect(() => {
Animated.timing(
animationReverse,
{
toValue: 0,
duration: 500,
useNativeDriver: false,
}
).start();
}, [animationReverse])
return (
<View>
{myCircles.map((index) => {
return (
<Animated.View
key={index}
style={[
{backgroundColor: 'red'},
(biggerCircleIndex == index)
? {width: animatedSize, height: animatedSize, borderRadius: 50}
: {width: animatedSizeReverse, height: animatedSizeReverse, borderRadius: 25},
]}>
</Animated.View>
);
})}
</View>
);
};
export {Circles};
CodePudding user response:
For achieving this, I think one of the best approaches is creating an independent Circle
component. That way, each circle would have its own Animated.Value
.
Also, since you only want to animate the radius
(height
and width
are the same value) and borderRadius
(which is half the radius
) we'd only need two Animated.Value
's.
We'd also need something to trigger the animation. In your example, I think the animations are triggered when the component mounts. Here I created an animate
prop for each circle, so that the animation runs when this prop changes.
The circle component could be something like this:
function Circle({ animate }) {
const radius = useRef(new Animated.Value(50)).current;
const borderRadius = Animated.divide(radius, 2);
useEffect(() => {
if (animate) {
// if this circle is selected, we animate it
Animated.timing(radius, {
toValue: 100,
duration: 500,
useNativeDriver: false,
}).start();
}
// if it isn't being selected we animate it back
else {
Animated.timing(radius, {
toValue: 50,
duration: 500,
useNativeDriver: false,
}).start();
}
}, [animate, radius]);
return (
<Animated.View
style={{
backgroundColor: 'blue',
height: radius,
width: radius,
borderRadius: borderRadius,
}}
/>
);
}
We need only one useEffect
which runs when animate
changes. If it changes to true
we should trigger the growing animation, if it changes to false
we should trigger the shrinking animation.
Then, the parent component (Circles
) could just control which circle is selected. I wrapped each circle in a Touchable
just so it's easier to change the selected circle, and to trigger the animations:
const Circles = (props) => {
const myCircles = [0, 1, 2, 3, 4, 5];
const [biggerCircleIndex, setBiggerCircleIndex] = useState(2);
return (
<View>
{myCircles.map((index) => {
return (
<TouchableOpacity onPress={() => setBiggerCircleIndex(index)}>
<Circle animate={index === biggerCircleIndex} key={`${index}`} />
</TouchableOpacity>
);
})}
</View>
);
};
There are probably other approaches to achieving this, maybe you could, as you suggested, make this work by creating an array of Animated.Value
's.
I hope this helps!
Uploaded this code to a snack.