Animations in React Native Applications

vansh Kapoorvansh Kapoor
4 min read

Our cold engines were just getting started during the standup on a Monday morning when our UI designer presented us an unearthly animated design (literally making us question our life choices) for a custom progress bar; seeing that me and my senior UI dev were scratching our heads like how will we even make it.
It took hundreds of chai and hours of writing Maths to prepare equations for getting the animation perfect.

Though sadly its an intellectual property of the client and I can't share the details here (cons of service based products),

a very sad meme. not cracking a joke, even I cried that day for the  Africans. - Keywarriors of Cricket - Quora

but no worries, I have written a generic and user friendly animated progress bar entirely different from what we did, which can be easily integrated in any of your project or client application if building forms on your app.
You can checkout the code from here github link. Would love to see people build on it for their use cases.

See a working Demo View here.

Hashnode still doesnt support adding gifs/videos so bear with me on drive video links 😔.

To understand how to build it, lets dive deep into how Animations work in React Native!

Getting started with Animations

In React Native, Animations are implemented using Animated imported from react-native pkg.

There are few common types of animation styles you can add:

Lets say I want to hide a View on click.

import { useState } from 'react';
import { View, StyleSheet, Button } from 'react-native';

export const TestAnimations = () => {
    const [opacity, setOpacity] = useState(1);

    return (
        <>
            <View style={styles.parentView}>
                <View style={[styles.redBox, { opacity: opacity }]}>
                </View>
            </View>
            <Button title='Hide it!' onPress={() => setOpacity(0)}></Button>
        </>
    );
};

const styles = StyleSheet.create({
    parentView: {
        alignItems: "center",
        marginBottom: 10,
    },
    redBox: {
        width: 50,
        height: 50,
        backgroundColor: "red",
    }
})

See output video here.

Alright, Time to animate it such that the View slowly fades away.

import { useState } from 'react';
import { View, StyleSheet, Button } from 'react-native';

export const TestAnimations = () => {
    const [opacity, setOpacity] = useState(1);

    const animateHideTimeout = async () => {
        const timeoutPromise = () => new Promise((res, _rej) => {
            setTimeout(() => {
                setOpacity(prevOpacity => prevOpacity-0.2);
                res();
            }, 100);
        });
        if (opacity >= 0) {
            await timeoutPromise(); //await is necessary otherwise max call stack error is thrown
            animateHideTimeout();
        }
    };

    return (
        <>
            <View style={styles.parentView}>
                <View style={[styles.redBox, { opacity: opacity }]} />
            </View>
            <Button title="Hide it!" onPress={animateHideTimeout} />
        </>
    );
};

const styles = StyleSheet.create({
    parentView: {
        alignItems: 'center',
        marginBottom: 10,
    },
    redBox: {
        width: 50,
        height: 50,
        backgroundColor: 'red',
    },
});

See output vide here.

That doesn't look very smooth, RIGHT!!

Well because we cant use useState hook for such animation tasks where the value has to change constantly. This will make our app laggy and slow since everything will run on the main js thread.

To avoid this we will use refs and custom animated state objects `Animated.Value(0)`. They special designed to handle animations on the UI Thread.

Let's see the code now.

we will change the initialisation to use Animated.value() instead of useState.

     // const [opacity, setOpacity] = useState(1);
    const opacityAnim = useRef(new Animated.Value(1)).current;

we will remove our timer hide method and we will use Animated.timing method. It goes like this,

    const animateHide = () => {
        Animated.timing(opacityAnim, // your animated value defined
        {
            toValue: 0, // target value
            duration: 1000,
            useNativeDriver: true, // make it true if we want animation to run on UI thread and not main thread. 
        }).start();
    };

useNativeDriver: true as we dont want to run our animation on the main thread.

Lastly, the Animated.Value() can't be used on basic View. So we will replace them with Animated.View.

<Animated.View style={[styles.redBox, { opacity: opacityAnim }]} />

Our Final code would look like this:

/* eslint-disable prettier/prettier */
import { useRef } from 'react';
import { View, StyleSheet, Button, Animated } from 'react-native';

export const TestAnimations = () => {
    // const [opacity, setOpacity] = useState(1);
    const opacityAnim = useRef(new Animated.Value(1)).current;

    const animateHide = () => {
        Animated.timing(opacityAnim, {
            toValue: 0,
            duration: 1000,
            useNativeDriver: true,
        }).start();
    };

    return (
        <>
            <View style={styles.parentView}>
                <Animated.View style={[styles.redBox, { opacity: opacityAnim }]} />
            </View>
            <Button title="Hide it!" onPress={animateHide} />
        </>
    );
};

const styles = StyleSheet.create({
    parentView: {
        alignItems: 'center',
        marginBottom: 10,
    },
    redBox: {
        width: 50,
        height: 50,
        backgroundColor: 'red',
    },
});

See Output video here.


Well that's a wrap! Know that we know how to build animations in React Native, you are ready to see the code for the complicated progress bar. Go ahead and checkout the code from here github link..

Happy Coding🎉

0
Subscribe to my newsletter

Read articles from vansh Kapoor directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

vansh Kapoor
vansh Kapoor

Developing large scale application for multiple clients in Thoughtworks. Love to share my knowledge in React, Js and clean coding paractices and travel.