In Part 1 we learned some fundamentals of react-native-reanimated, its declarative syntax and its working under the hood by creating a simple Accordion. If you missed the first part, it will be better if you visit it and come back here as lots of concepts covered there will be referred here in this article.

In this blog, we'll understand how to perform translation transformation by creating a shiny effect on the progress bar using react-native-reanimated.

So, let's get started.

Pre-requisites:

Initialize project:

Let's create our project by running:

react-native init rnr-shiny-progress-bar

We will be using the following dependencies to create our app:

Next, we install react-native-reanimated as follows:

yarn add react-native-reanimated

For iOS, we install pod dependencies for react-native-reanimated as follows:

cd ./ios && pod install

Creating a Progress bar:

We first create a simple Progress bar. react-native-reanimated has an Examples folder which consists of the source code for creating a progress bar. After copying the code from index.js to App.js, we do some minor changes like removing Buttons as they are not needed in our case. With these changes, our App.js looks as follows:

Explanation

In brief, here we have a state variable called progress initialized to 0. On mount of the App component, progress gets updated by 0.1 after every interval of 5 seconds. This progress is sent as props to ProgressBar component. As soon as it reaches 1, we clear the interval. This implies that we have completed 100% progress.

Next, in our root/src/components, we create a component called ProgressBar. Our folder structure looks as follows:

folder structure after adding ProgressBar

We then copy the code from ProgressBar.js to our ProgressBar component, change defaultProps for height and width and make some style changes. Our ProgressBar component's code looks as follows:

Explanation

  • Lines 7-19 extract necessary nodes from react-native-reanimated's Animated API.
  • Lines 21-55 is the wrapper runTiming() which is responsible for animation of ProgressBar. It is similar to the one explained in the previous part.
  • Lines 73-75 initializes necessary nodes for the animation.
  • At line 76 we call runTiming() with necessary parameters and it returns a block of nodes which is called frame by frame as explained previously.

In render(),

  • Lines 84-89 we describe the style for the progress bar. At line 85 we use concat() to concatenate position node's value returned by this.transX with %.
  • On line 101 we apply this style to the Animated.View.

As progress updates after an interval, in componentDidUpdate() we set the value of this.animation to the current value of progress multiplied by 100 (as we want to express width in %). Since this.animation is one of the parameters of runTiming(), the block returned by runTiming() is called on every update of this.animation and position node is updated to the current value of this.animation provided as dest frame by frame.

Our ProgressBar looks as follows:

progressbar

Adding a shiny effect:

Let us add a shiny effect to the Progress Bar. We'll create a shiny effect using
Gradients.
react-native-linear-gradient is a useful package that provides a LinearGradient component for react-native.

We first install react-native-linear-gradient as follows:

yarn add react-native-linear-gradient

For iOS, we install pod dependencies for react-native-linear-gradient as follows:

cd ./ios && pod install

Next, in our root/src/components folder we add a component called ShinyEffect.

folder structure after adding ShinyEffect component

We import the following necessary components and APIs

and extract following nodes from react-native-reanimated's Animated API.

Next, we add wrapper called runTranslationTiming() as follows:

Explanation

runTranslationTiming() is similar to runTiming() with one difference that is, it runs repeatedly.

  • Lines 25-30 inside the block node check whether the animation is finished by checking whether the flag state.finished is set to 1. If it is set to 1 we reset
    • state.finished
    • state.time
    • state.frameTime
    • state.position

Since the clock has not been stopped, the animation keeps on repeating.

The code for ShinyEffect component is as follows:

  • Line 4 defines Animated value range which will be useful for translation later.
  • Line 6 calls runTranslationTiming() with clock, value and dest parameters assigned to 0 and 5000 respectively. Note, these values can be as per your choice and are responsible for user experience. So choose them appropriately. The block returned by runTranslationTiming() is assigned to this.transX.
  • Next, inside the constructor(), we use interpolate on state.position returned by blocks to this.transX. In animation, interpolation is the process of inbetweening which gives an illusion of movement or motion between two images. Here, we interpolate state.position from inputRange (0,5000) to outputRange (-500, progress fraction of the width of the bar). Again, the inputRange and outputRange values have been decided based on user experience. We also interpolate state.position on componentDidUpdate() as well i.e. when progress updates. The output mapping based on input is returned to this.range.
  • Lines 25-56 render our ShinyEffect.
    • At line 34 we assign this.range to translateX style prop of the Animated.View and that's how we perform translation using interpolation.

In ProgressBar component we add ShinyEffect only when progress is > 0 as follows:

Explanation

As the progress changes, the block returned by runTranslationTiming() to this.transX executes. It updates the state.position using timing() as explained in the previous blog. Based on the value of state.position in inputRange, its mapped value from the outputRange is returned to this.range which is later mapped to translateX style prop of Animated.View.

And we have a shiny effect on our ProgressBar.

shiny effect on ProgressBar

Hope this blog helped you in understanding how translation transformation can be performed using react-native-reanimated.

You can clone this Github repo and experiment with the same.

For the version of code in hooks, switch to the branch hooks-version in the repo.

Thank you for reading.

References: