is it bad to have multiple animation variables?

43 views Asked by At

I had a question about Animations with React Native, I'm pretty new to these. Currently, my animations are working, but only if I have 1 at a time. If I introduce another at the same time, it will run smoothly for about 2 minutes, then suddenly the JavaScript thread will drop from around 55fps to about 5fps (I'm not running anything else during these 2 minutes). RAM usage looks pretty constant. My animated component has this code for animating:

const ANIMATION_DURATION = 900;
const frames = useMemo(
    () => grabFrames(props.teaFriendID), [props.teaFriendID]
);

if (!props.isAnimated) {
    return (
        <Image
            source={frames[0]}
        />
    );
}
else {
    const progress = useRef(new Animated.Value(0)).current;
    const [frameIndex, setFrameIndex] = useState(0);
    useEffect(() => {
        const animation = Animated.loop(
            Animated.timing(progress, {
                toValue: 2.99,
                duration: ANIMATION_DURATION,
                useNativeDriver: true,
            }),
        );
        animation.start();

        return () => {
            animation.stop();
            progress.setValue(0);
        };
    }, [progress])

    progress.addListener(({ value }) => {
        const newIndex = Math.floor(value);
        setFrameIndex(newIndex);
    });

    return (
        <Animated.Image
            style={GLOBAL_STYLES.friendHome}
            source={frames[frameIndex]}
        />
    );

My app will have up to 4 of these components rendered. As soon as I have more than 1, it doesn't get too laggy at first (using an emulator too) but it will tank significantly after maybe 2 minutes.

My main question was whether having multiple animated values is a bad thing and whether there's any specific interactions that could be causing it to crash, or whether it is just too much for React Native to handle. Any other suggestions to my code would also be appreciated.

1

There are 1 answers

2
Iman Maleki On

Adding the listener to useEffect and removing 'progress' from useEffect dependencies would improve performance.

However, for costly animations, using 'react-native-reanimated' is a much better solution, as they run on the UI thread.

const ANIMATION_DURATION = 900;

const AnimatedImages = (props) => {
  const frames = useMemo(() => grabFrames(props.teaFriendID), [props.teaFriendID]);

  if (!props.isAnimated) return <Image source={frames[0]} />;

  const progress = useRef(new Animated.Value(0)).current;
  const [frameIndex, setFrameIndex] = useState(1);

  useEffect(() => {
    Animated.loop(
      Animated.timing(progress, {
        toValue: 2.99,
        duration: ANIMATION_DURATION,
        useNativeDriver: false,
      })
    ).start();

    progress.addListener(({ value }) => {
      const newIndex = Math.floor(value);
      setFrameIndex(newIndex);
    });

    return () => {
      progress.removeAllListeners();
    };
  }, []);

  return (
    <Animated.Image style={GLOBAL_STYLES.friendHome} source={frames[frameIndex]} />
  );
};