React Native Pulse( Tinder inspired) Animation with Reanimated 2

Create Pulse animation with Reanimated 2

React Native Pulse( Tinder inspired) Animation with Reanimated 2

In this post,  I m going to create and make a detailed explanation of how we can create pulse animation with Reanimated library.

Let's dive into the code.

First of all, I created a component named Pulse, it's a circle

const Pulse = () => {
  return <Animated.View style={styles.circle} />;
};

const styles = StyleSheet.create({
  circle: {
    width: 300,
    borderRadius: 150,
    height: 300,
    position: 'absolute',
    borderColor: '#e91e63',
    borderWidth: 4,
    backgroundColor: '#ff6090',
  },
});

and inside the main App component, I used Image and wrapped it with a container view.

Now we can add animation to the Pulse component. For the animation we use  withTiming and withRepeat animation and useAnimatedStyle  hook.

  // Create shared Value
  const animation = useSharedValue(0);
  
  // We repeatedly doing shared value from 0 to 1
  useEffect(() => {
    animation.value = 
      withRepeat(
        withTiming(1, {
          duration: 2000,
          easing: Easing.linear,
        }),
         -1,
        false
      );
  }, []);
  
   // when component mount. scale the component from 0 to 1.
  // also we used interpolate for decreasing opacity. 0.6 to 0
  
  const animatedStyles = useAnimatedStyle(() => {
    const opacity = interpolate(
      offset.value,
      [0, 1],
      [0.6, 0],
      Extrapolate.CLAMP
    );
    return {
      opacity: opacity,
      transform: [{ scale: offset.value }],
    };
  });

it's simple right? now we have one circle animation. but let's add one more feature.

When the user clicks the image add new animation to the view.

I create a state named pulse.

 const [pulse, setPulse] = useState([1]);

and wrapped Image component with Pressable

<Pressable
          style={styles.innerCircle}
          onPress={() => {
            setPulse((prev) => [...prev, Math.random()]);
          }}>
          <Image
            style={styles.innerCircle}
            source={{
              uri: 'https://reactnative.dev/img/tiny_logo.png',
            }}
          />
        </Pressable>

It randomly adds a number to the pulse array. Then I use the map function to return multiple pulse components. I added it because I wanted newly added animations to be displayed only once.

 {pulse.map((item, index) => (
          <Pulse repeat={index === 0} />
 ))}

const Pulse = ({repeat }) => {
  const animation = useSharedValue(0);
  useEffect(() => {
    animation.value = withDelay(
      delay,
      withRepeat(
        withTiming(1, {
          duration: 2000,
          easing: Easing.linear,
        }),
        repeat ? -1 : 1,
        false
      )
    );
  }, []);
  const animatedStyles = useAnimatedStyle(() => {
    const opacity = interpolate(
      animation.value,
      [0, 1],
      [0.6, 0],
      Extrapolate.CLAMP
    );
    return {
      opacity: opacity,
      transform: [{ scale: animation.value }],
    };
  });
  return <Animated.View style={[styles.circle, animatedStyles]} />;
};

That's it. Snack repo is here