DEV Community

Cover image for How to add image preloading animation to a react native component using reanimated2
Uwem
Uwem

Posted on

How to add image preloading animation to a react native component using reanimated2

Hello, how about we make our react native app a little bit fancy by adding preloading animations to our image component using the awesome reanimated2 library

We'll wrap the the Image component in an animated view having the same dimension as the image and create a state to manage the image status.

import React, { useEffect, useState } from "react";
import Animated, {
  interpolateColor,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from "react-native-reanimated";

interface Props {
  /**base64 string or url for the image */
  uri: string;
  width: number;
  height: number;
}
const AppImage = ({ height, uri, width }: Props) => {
  const [isImageLoading, setIsImageLoading] = useState(true);

  return (
    <Animated.View style={[{ width, height }]}>
      <Animated.Image
        {...{ height, width, source: { uri } }}
        style={[{ width, height }]
        resizeMethod="auto"
        resizeMode="cover"
      />
    </Animated.View>
  );
};

export default AppImage;
Enter fullscreen mode Exit fullscreen mode

The image opacity will be set to 0 on mounting while we animated the background of the animated view with a loop animation
When the image is loaded, we'll stop the loop animation and set the image opacity to 1

import React, { useEffect, useState } from "react";
import Animated, {
  interpolateColor,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from "react-native-reanimated";

interface Props {
  /**base64 string or url for the image */
  uri: string;
  width: number;
  height: number;
}
const AppImage = ({ height, uri, width }: Props) => {
  const [isImageLoading, setIsImageLoading] = useState(true);
  const loop = useSharedValue(0); // this will be used to control the animation of the image container

  const animatedContainerStyle = useAnimatedStyle(() => ({
    backgroundColor: interpolateColor(loop.value, [0, 1], ["black", "white"]), // animate the background color of the image container
  }));

  const animatedImageStyle = useAnimatedStyle(() => ({
    opacity: withTiming(isImageLoading ? 0 : 1, { duration: 500 }), // fade in image when loaded
  }));

  useEffect(() => {
    let animationInterval: NodeJS.Timeout;

    //start animation when the component mounts
    animationInterval = setInterval(() => {
      loop.value = withTiming(loop.value === 1 ? 0 : 1, { duration: 500 });
    }, 250);

    //stop animation when the image is loaded
    !isImageLoading && clearInterval(animationInterval);
    return () => clearInterval(animationInterval); //cleanup
  }, [isImageLoading]);

  return (
    <Animated.View style={[{ width, height }, animatedContainerStyle]}>
      <Animated.Image
        {...{ height, width, source: { uri } }}
        style={[{ width, height }, animatedImageStyle]}
        onLoad={(e) => {
          setIsImageLoading(false);
        }}
        resizeMethod="auto"
        resizeMode="cover"
      />
    </Animated.View>
  );
};

export default AppImage;
Enter fullscreen mode Exit fullscreen mode

Here is a preview

Preview

Top comments (0)