Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

398
Views
React Native: Fade through a series of images using Animated & opacity

I'm currently trying to fade through a series of images. Basically, I always want to display one image at a time, then animate its opacity from 1 to 0 and that of the next image in the series from 0 to 1, and so on. Basically something like this, which I've already implemented for the web in ReactJS and CSS animations:

Fade animation in ReactJS

However, I seem to keep getting stuck on using React Native's Animated library and refs. I've tried storing the opacity of all the images in an array which itself is contained in an useRef hook. Then, using Animated, I'm trying to perform two parallel animations which change the opacity of the current image index and that of the next index. This is what I've come up with:

export default function StartImageSwitcher() {
  const images = Object.values(Images).map((img) => img.imageNoShadow);

  const [currentImage, setCurrentImage] = useState(0);
  const opacity = useRef<Animated.Value[]>([
    new Animated.Value(1),
    ...Array(images.length - 1).fill(new Animated.Value(0)),
  ]).current;

  useEffect(() => {
    let nextImage = currentImage + 1;
    if (nextImage >= images.length) nextImage = 0;

    Animated.parallel([
      Animated.timing(
        opacity[currentImage],
        {
          toValue: 0,
          duration: 2000,
          useNativeDriver: true,
        },
      ),
      Animated.timing(
        opacity[nextImage],
        {
          toValue: 1,
          duration: 2000,
          useNativeDriver: true,
        },
      ),
    ]).start(() => {
      setCurrentImage(nextImage);
    });
  }, [currentImage]);

  images.map((image, index) => console.log(index, opacity[index]));

  return (
    <View style={styles.imageWrapper}>
      {
      images.map((image, index) => (
        <Animated.Image style={{ ...styles.image, opacity: opacity[index] }} source={image} key={index} />
      ))
    }
    </View>
  );
}

However, this doesn't seem to work at all. When mounted, it only shows the first image, then fades that one out and all the other images in and gets stuck there:

Image clutter using the above code in React Native

Anyone got an idea where I messed up? I feel like I'm not using the useRef() hook in combination with the Animated library like I'm supposed to.

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

Your solution is pretty clever, and it generally looks like it should work to me. Performance might take a hit though as the number of images increases. I thought of an alternate method: partition the images array and then use setInterval to alternate between two Animated.Images, which get their source from each array.

// from https://stackoverflow.com/questions/41932345/get-current-value-of-animated-value-react-native
const getAnimatedValue = (value: Animated.Value) => Number.parseInt(JSON.stringify(value));

export default function StartImageSwitcher() {
  // partition images into two arrays
  const images1 = [];
  const images2 = [];
  Object.values(Images).forEach((img, i) => {
    (i % 1 === 0 ? images1 : images2).push(img.imageNoShadow)
  });

  // use refs for the indexes so values don't become stale in the setInterval closure
  const images1Index = useRef(0);
  const images2Index = useRef(0);
  const image1Opacity = useRef(new Animated.Value(1)).current;
  const image2Opacity = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    const swapImageInterval = setInterval(() => {
      const newImage1Opacity = getAnimatedValue(image1Opacity) === 1 ? 0 : 1;
      const newImage2Opacity = getAnimatedValue(image2Opacity) === 1 ? 0 : 1;

      Animated.parallel([
        Animated.timing(image1Opacity, {
          toValue: newImage1Opacity,
          duration: 2000,
          useNativeDriver: true,
        }),
        Animated.timing(image2Opacity, {
          toValue: newImage2Opacity,
          duration: 2000,
          useNativeDriver: true,
        }),
      ]).start(() => {
        if (newImage1Opacity === 1) {
          // image 2 is now faded out, so we can swap out its source
          const nextIndex = images2Index.current === images2.length - 1 ? 0 : images2Index.current + 1;
          images2Index.current = nextIndex;
        } else {
          // image 1 is faded out, so we can swap out its source 
          const nextIndex = images1Index.current === images1.length - 1 ? 0 : images1Index.current + 1;
          images1Index.current = nextIndex;
        }
      })
    }, 5000)

    return () => clearInterval(swapImageInterval);
  }, [images1Index, images2Index]);

  return (
    <View style={styles.imageWrapper}>
      <Animated.Image style={{ ...styles.image, opacity: image1Opacity }} source={images1[images1Index.current]} />
      <Animated.Image style={{ ...styles.image, opacity: image2Opacity }} source={images2[images2Index.current]} />
    </View>
  );
}
about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!