Android – An Overview Of Property Animation In Android

Hello Readers, CoolMonkTechie heartily welcomes you in this article (An Overview Of Property Animation In Android).

In this article, we will learn about Property Animation Overview in Android. The property animation system is a robust framework that allows us to animate almost anything. We can define an animation to change any object property over time, regardless of whether it draws to the screen. A property animation changes a property’s (a field in an object) value over a specified length of time. To animate something, we specify the object property that we want to animate, such as an object’s position on the screen, how long we want to animate it for, and what values we want to animate between. This article shows the Property Animation related concepts in Android.

A famous quote about learning is :

” Develop a passion for learning. If you do, you will never cease to grow. “

So let’s begin.

Characteristics Of Property Animation

The property animation system lets us define the following characteristics of an animation:

  • Duration: We can specify the duration of an animation. The default length is 300 ms.
  • Time interpolation: This specify how the values for the property calculate as a function of the animation’s current elapsed time.
  • Repeat count and behavior: This specify whether to have an animation repeat when it reaches the end of a duration and how many times to repeat the animation. We can also specify whether we want the animation to play back in reverse. Setting it to reverse plays the animation forwards then, backwards repeatedly, until it reaches the number of repeats.
  • Animator sets: We can group animations into logical sets that play together or sequentially or after specified delays.
  • Frame refresh delay: This specify how often to refresh frames of our animation. The default is set to refresh every 10 ms, but the speed in which our application can refresh frames is ultimately dependent on how busy the system is overall and how fast the system can service the underlying timer.

The Work Flow of Property Animation

Linear Animation

First, we see how an animation works with a simple example.

Source: Android Developer – The Example Of Linear Animation

In this above linear animation figure, it depicts a hypothetical object animates with its x property, which represents its horizontal location on a screen. It sets the duration of the animation to 40 ms and the distance to travel is 40 pixels. Every 10 ms, which is the default frame refresh rate, the object moves horizontally by 10 pixels. At the end of 40ms, the animation stops, and the object ends at horizontal position 40. This is an example of an animation with linear interpolation, meaning the object moves at a constant speed.

Non-Linear Animation

We can also specify animations to have a non-linear interpolation.

Source: Android Developer – The Example of a non-linear animation

In this above non-linear animation figure, it illustrates a hypothetical object that speeds up at the beginning of the animation and decelerates at the end of the animation. The object still moves 40 pixels in 40 ms, but non-linearly. In the beginning, this animation accelerates up to the halfway point, then decelerates from the halfway point until the end of the animation. As this non-linear animation figure shows the distance traveled at the beginning and end of the animation is less than in the middle.

Animations Calculation Using Property Animation

We understood how the important components of the property animation system would calculate animations like the ones. Now we will discuss how the main classes work with one another.

Source: Android Developer – How animations are calculated

In this figure, The ValueAnimator object keeps track of our animation’s timing, such as how long the animation has been running, and the current value of the property that it is animating.

The ValueAnimator encapsulates

  • TimeInterpolator, which defines animation interpolation, and
  • TypeEvaluator, which defines how to calculate values for the property being animated.

For example, in non-linear animation figure, the TimeInterpolator used would be AccelerateDecelerateInterpolator and the TypeEvaluator would be IntEvaluator.

To start an animation, we create a ValueAnimator and give it the starting and ending values for the property that we want to animate, along with the duration of the animation. When we call start(), the animation begins. During the whole animation, the ValueAnimator calculates an elapsed fraction between 0 and 1, based on the duration of the animation and how much time has elapsed. The elapsed fraction represents the percentage of time that the animation has completed, 0 meaning 0% and 1 meaning 100%. For example, in linear animation figure , the elapsed fraction at t = 10 ms would be .25 because the total duration is t = 40 ms.

When the ValueAnimator calculates an elapsed fraction, it calls the TimeInterpolator that is currently set to calculate an interpolated fraction. An interpolated fraction maps the elapsed fraction to a new fraction that takes into account the time interpolation that is set. For example, in non-linear animation figure, because the animation slowly accelerates, the interpolated fraction, about .15, is less than the elapsed fraction, .25, at t = 10 ms. In linear animation figure, the interpolated fraction is always the same as the elapsed fraction.

When the interpolated fraction calculates, the ValueAnimator calls the TypeEvaluator to calculate the value of the property that you are animating, based on the interpolated fraction, the starting value, and the ending value of the animation. For example, in non-linear figure, the interpolated fraction was .15 at t = 10 ms, so the value for the property would be .15 × (40 – 0), or 6.

Property Animation Vs View Animation

View Animation

” The View Animation system provides the capability to only animate View objects.”

So if we wanted to animate non-View objects, we have to implement our own code to do so.

“The View Animation system constrains because it only exposes a few aspects of a View object to animate, such as the scaling and rotation of a View but not the background color, for instance.”

“Another disadvantage of the View Animation system is that it only modified where the View drew, and not the actual View itself.”

For instance, if we animated a button to move across the screen, the button draws correctly, but the actual location where we can click the button does not change, so we have to implement our own logic to handle this.

Property Animation

“With the property animation system, these constraints removed completely, and we can animate any property of any object (Views and non-Views) and the object they modify it.”

The property animation system is also more robust in the way it carries out animation. At a high level, we assign animators to the properties that we want to animate, such as color, position, or size and can define aspects of the animation such as interpolation and synchronization of multiple animators.

The View Animation system, however, takes less time to set up and requires less code to write. If View Animation accomplishes everything that we need to do, or if our existing code already works the way we want, there is no need to use the Property Animation system. It also might make sense to use both animation systems for different situations if the use case arises.

The Main Components of Property Animation System

We can find most of the property animation system’s APIs in android.animation. Because the View Animation system already defines many interpolators in android.view.animation, we can use those interpolators in the property animation system.

The Main Components of Property Animation System are :

  • Animators
  • Evaluators
  • Interpolators

Animators

The Animator class provides the basic structure for creating animations. We normally do not use this class directly, as it only provides minimal functionality that must be extended to fully support animating values. The following subclasses extend Animator:

  • ValueAnimator
  • ObjectAnimator
  • AnimatorSet

ValueAnimator

The main timing engine for property animation that also computes the values for the property to be animated. It has all the core functionality that calculates animation values and contains the timing details of each animation, information about whether an animation repeats, listeners that receive update events, and the ability to set custom types to evaluate.

There are two pieces to animating properties: 

  • calculating the animated values and
  • setting those values on the object and property that is being animated.

 ValueAnimator does not carry out the second piece, so we must listen for updates to values calculated by the ValueAnimator and modify the objects that want to animate with our own logic.

ObjectAnimator

A subclass of ValueAnimator that allows us to set a target object and object property to animate. This class updates the property accordingly when it computes a new value for the animation.

We want to use ObjectAnimator most of the time, because it makes animating values on target objects much easier. However, we sometimes want to use ValueAnimator directly because ObjectAnimator has a few more restrictions, such as requiring specific accessor methods to be present on the target object.

AnimatorSet

This provides a mechanism to group animations together so they run in relation to one another. We can set animations to play together, sequentially, or after a specified delay. 

Evaluators

Evaluators tell the property animation system how to calculate values for a property. They take the timing data that an Animator class provides, the animation’s start and end value, and calculate the animated values of the property based on this data.

The property animation system provides the following evaluators:

  • IntEvaluator: The default evaluator to calculate values for int properties.
  • FloatEvaluator: The default evaluator to calculate values for float properties.
  • ArgbEvaluator: The default evaluator to calculate values for color properties that represents as hexadecimal values.
  • TypeEvaluator: An interface that allows us to create our own evaluator. If we are animating an object property that is not an intfloat, or color, we must implement the TypeEvaluator interface to specify how to compute the object property’s animated values. We can also specify a custom TypeEvaluator for intfloat, and color values, if we want to process those types differently than the default behavior.

Interpolators

A time interpolator defines how specific values in an animation are calculated as a function of time.

For example, we can specify animations to happen linearly across the whole animation, meaning the animation moves evenly the entire time, or we can specify animations to use non-linear time, for example, accelerating at the beginning and decelerating at the end of the animation.

The property animation system provides the following interpolators:

  • AccelerateDecelerateInterpolator: An interpolator whose rate of change starts and ends slowly but accelerates through the middle.
  • AccelerateInterpolator: An interpolator whose rate of change starts out slowly and then accelerates.
  • AnticipateInterpolator: An interpolator whose change starts backward, then flings forward.
  • AnticipateOvershootInterpolator: An interpolator whose change starts backward, flings forward and overshoots the target value, then finally goes back to the final value.
  • BounceInterpolator: An interpolator whose change bounces at the end.
  • CycleInterpolator: An interpolator whose animation repeats for a specified number of cycles.
  • DecelerateInterpolator: An interpolator whose rate of change starts out quickly and then decelerates.
  • LinearInterpolator: An interpolator whose rate of change is constant.
  • OvershootInterpolator: An interpolator whose change flings forward and overshoots the last value then comes back.
  • TimeInterpolator: An interface that allows you to implement your own interpolator.

Animate Using ValueAnimator

The ValueAnimator class lets us animate values of some type for the duration of an animation by specifying a set of intfloat, or color values to animate through. We get a ValueAnimator by calling one of its factory methods: ofInt()ofFloat(), or ofObject(). For example:

ValueAnimator.ofFloat(0f, 100f).apply {
    duration = 1000
    start()
}

In this code, the ValueAnimator calculates the values of the animation, between 0 and 100, for a duration of 1000 ms, when the start() method runs.

We can also specify a custom type to animate by doing the following code:

ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply {
    duration = 1000
    start()
}

In this code, the ValueAnimator calculates the values of the animation, between startPropertyValue and endPropertyValue using the logic supplied by MyTypeEvaluator for a duration of 1000 ms, when the start() method runs.

We can use the values of the animation by adding an AnimatorUpdateListener to the ValueAnimator object, as shown in the following code:

ValueAnimator.ofObject(...).apply {
    ...
    addUpdateListener { updatedAnimation ->
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        textView.translationX = updatedAnimation.animatedValue as Float
    }
    ...
}

In the onAnimationUpdate() method, we can access the updated animation value and use it in a property of one of our views.

Animate Using ObjectAnimator

The ObjectAnimator is a subclass of the ValueAnimator and combines the timing engine and value computation of ValueAnimator with the ability to animate a named property of a target object. This makes animating any object much easier, as we no longer need to implement the ValueAnimator.AnimatorUpdateListener, because the animated property updates automatically.

Instantiating an ObjectAnimator is like a ValueAnimator, but we also specify the object and the name of that object’s property (as a String) along with the values to animate between:

ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
    duration = 1000
    start()
}

To have the ObjectAnimator update properties correctly, we must do the following steps:

  • The object property that we are animating must have a setter function (in camel case) in the form ofset<PropertyName>(). Because the ObjectAnimator automatically updates the property during animation, it must be able to access the property with this setter method. For example, if the property name is foo, we need to have a setFoo() method. If this setter method does not exist, we have three options:
    • Add the setter method to the class if we have the rights to do so.
    • Use a wrapper class that we have rights to change and have that wrapper receive the value with a valid setter method and forward it to the original object.
    • Use ValueAnimator instead.
  • If we specify only one value for the values... parameter in one of the ObjectAnimator factory methods, we assume it to be the ending value of the animation. Therefore, the object property that we are animating must have a getter function that is used to get the starting value of the animation. The getter function must be in the form of get<PropertyName>(). For example, if the property name is foo, we need to have a getFoo() method.
  • The getter and setter methods of the property that we are animating must operate on the same type as the starting and ending values that specify to ObjectAnimator. For example, we must have targetObject.setPropName(float) and targetObject.getPropName() if we construct the following ObjectAnimator:
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
  • Depending on what property or object we are animating, we might need to call the invalidate() method on a View to force the screen to redraw itself with the updated animated values. We do this in the onAnimationUpdate() callback.

Animate Using AnimatorSet

In many cases, we want to play an animation that depends on when another animation starts or finishes. The Android system lets us bundle animations together into an AnimatorSet, so that we can specify whether to start animations simultaneously, sequentially, or after a specified delay. We can also nest AnimatorSet objects within each other.

val bouncer = AnimatorSet().apply {
    play(bounceAnim).before(squashAnim1)
    play(squashAnim1).with(squashAnim2)
    play(squashAnim1).with(stretchAnim1)
    play(squashAnim1).with(stretchAnim2)
    play(bounceBackAnim).after(stretchAnim2)
}
val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
}
AnimatorSet().apply {
    play(bouncer).before(fadeAnim)
    start()
}

In this code snippet, the following Animator objects in the following manner :

  • Plays bounceAnim.
  • Plays squashAnim1squashAnim2stretchAnim1, and stretchAnim2 at the same time.
  • Plays bounceBackAnim.
  • Plays fadeAnim.

Animation Listeners

We can listen for important events during an animation’s duration with the listeners described below.

  • Animator.AnimatorListener
  • ValueAnimator.AnimatorUpdateListener

Animator.AnimatorListener

  • onAnimationStart() : This method called when the animation starts.
  • onAnimationEnd(): This called when the animation ends.
  • onAnimationRepeat(): This method called when the animation repeats itself.
  • onAnimationCancel(): Called when the animation is canceled. A cancelled animation also calls onAnimationEnd(), regardless of how they were ended.

ValueAnimator.AnimatorUpdateListener

onAnimationUpdate(): This method called on every frame of the animation. Listen to this event to use the calculated values generated by ValueAnimator during an animation.

Animate Layout Changes to ViewGroup Objects

The property animation system provides the capability to animate changes to ViewGroup objects as well as provide an easy way to animate View objects themselves.

We can animate layout changes within a ViewGroup with the LayoutTransition class. Views inside a ViewGroup can go through an appearing and disappearing animation when we add them to or remove them from a ViewGroup or when we call a View’s setVisibility() method with VISIBLEINVISIBLE, or GONE. The remaining Views in the ViewGroup can also animate into their new positions when we add or remove Views.

We can define the following animations in a LayoutTransition object by calling setAnimator() and passing in an Animator object with one of the following LayoutTransition constants:

  • APPEARING : This flag indicating the animation that runs on items that are appearing in the container.
  • CHANGE_APPEARING : A flag indicating the animation that runs on items that are changing due to a new item appearing in the container.
  • DISAPPEARING : This flag indicating the animation that runs on items that are disappearing from the container.
  • CHANGE_DISAPPEARING : A flag indicating the animation that runs on items that are changing due to an item disappearing from the container.

Animate View State Changes Using StateListAnimator

The StateListAnimator class lets us define animators that run when the state of a view changes. This object behaves as a wrapper for an Animator object, calling that animation whenever the specified view state (such as “pressed” or “focused”) changes.

The StateListAnimator can be defined in an XML resource with a root <selector> element and child <item> elements that each specify a different view state defined by the StateListAnimator class. Each <item> contains the definition for a property animation set.

For example, the following file creates a state list animator that changes the x and y scale of the view when it’s pressed:

res/xml/animate_scale.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- the pressed state; increase x and y size to 150% -->
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
        </set>
    </item>
    <!-- the default, non-pressed state; set x and y size to 100% -->
    <item android:state_pressed="false">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

To attach the state list animator to a view, add the android:stateListAnimator attribute as follows:

<Button android:stateListAnimator="@xml/animate_scale"
        ... />

Now the animations defined in animate_scale.xml are used when this button’s state changes.

Or, to instead assign a state list animator to a view in our code, use the AnimatorInflater.loadStateListAnimator() method, and assign the animator to our view with the View.setStateListAnimator() method.

Use a TypeEvaluator

If we want to animate a type unknown to the Android system, we can create our own evaluator by implementing the TypeEvaluator interface. The types that are known by the Android system are intfloat, or a color, which are supported by the IntEvaluatorFloatEvaluator, and ArgbEvaluator type evaluators.

There is only one method to implement in the TypeEvaluator interface, the evaluate() method. This allows the animator that we are using to return an appropriate value for our animated property at the current point of the animation. The FloatEvaluator class shows how to do this:

private class FloatEvaluator : TypeEvaluator<Any> {

    override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any {
        return (startValue as Number).toFloat().let { startFloat ->
            startFloat + fraction * ((endValue as Number).toFloat() - startFloat)
        }
    }

}

Use Interpolators

An interpolator define how calculate specific values in an animation as a function of time. For example, we can specify animations to happen linearly across the whole animation, meaning the animation moves evenly the entire time, or we can specify animations to use non-linear time, for example, using acceleration or deceleration at the beginning or end of the animation.

Interpolators in the animation system receive a fraction from Animators that represent the elapsed time of the animation. Interpolators modify this fraction to coincide with the type of animation that it aims to provide. The Android system provides a set of common interpolators in the android.view.animation package. If none of these suit our needs, we can implement the TimeInterpolator interface and create our own.

For example, we will see that how to compare the default interpolator AccelerateDecelerateInterpolator and the LinearInterpolator that calculates interpolated fractions. The LinearInterpolator has no effect on the elapsed fraction. The AccelerateDecelerateInterpolator accelerates into the animation and decelerates out of it.

AccelerateDecelerateInterpolator

override fun getInterpolation(input: Float): Float =
        (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f

LinearInterpolator

override fun getInterpolation(input: Float): Float = input

Specify Keyframes

Keyframe object consists of a time/value pair that lets us define a specific state at a specific time of an animation. Each keyframe can also have its own interpolator to control the behavior of the animation in the interval between the previous keyframe’s time and the time of this keyframe.

To instantiate a Keyframe object, we must use one of the factory methods, ofInt()ofFloat(), or ofObject() to get the appropriate type of Keyframe. We then call the ofKeyframe() factory method to get a PropertyValuesHolder object. Once we have the object, we can get an animator by passing in the PropertyValuesHolder object and the object to animate. 

val kf0 = Keyframe.ofFloat(0f, 0f)
val kf1 = Keyframe.ofFloat(.5f, 360f)
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
    duration = 5000
}

Animate Views

The Property Animation system allows streamlined animation of View objects and offers a few advantages over the view animation system. The View Animation system transformed View objects by changing the way that they were drawn. This was handled in the container of each View, because the View itself had no properties to manipulate. This resulted in the View being animated, but caused no change in the View object itself. This led to behavior such as an object still existing in its original location, even though it was drawn on a different location on the screen.

The property animation system can animate Views on the screen by changing the actual properties in the View objects. In addition, Views also automatically calls the invalidate() method to refresh the screen whenever its properties are changed. The new properties in the View class that facilitate property animations are:

  • translationX and translationY: These properties control where the View is located as a delta from its left and top coordinates, which are set by its layout container.
  • rotationrotationX, and rotationY: These properties control the rotation in 2D (rotation property) and 3D around the pivot point.
  • scaleX and scaleY: These properties control the 2D scaling of a View around its pivot point.
  • pivotX and pivotY: These properties control the location of the pivot point, around which the rotation and scaling transforms occur. By default, the pivot point is located at the center of the object.
  • x and y: These are simple utility properties to describe the final location of the View in its container, as a sum of the left and top values and translationX and translationY values.
  • alpha: Represents the alpha transparency on the View. This value is 1 (opaque) by default, with a value of 0 representing full transparency (not visible).

To animate a property of a View object, such as its color or rotation value, all we need to do is create a property animator and specify the View property that we want to animate. For example:

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)

Animate Using ViewPropertyAnimator

The ViewPropertyAnimator provides a simple way to animate several properties of a View in parallel, using a single underlying Animator object. It behaves much like an ObjectAnimator, because it modifies the actual values of the view’s properties, but is more efficient when animating many properties at once. In addition, the code for using the ViewPropertyAnimator is much more concise and easier to read.

The following code snippets show the differences in using multiple ObjectAnimator objects, a single ObjectAnimator, and the ViewPropertyAnimator when simultaneously animating the x and y property of a view.

Multiple ObjectAnimator objects

val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
    playTogether(animX, animY)
    start()
}

One ObjectAnimator

val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()

ViewPropertyAnimator

myView.animate().x(50f).y(100f)

Declare Animations in XML

The property animation system lets us declare property animations with XML instead of doing it programmatically. By defining our animations in XML, we can easily reuse our animations in multiple activities and more easily edit the animation sequence.

The following property animation classes have XML declaration support with the following XML tags:

ValueAnimator - <animator>
ObjectAnimator - <objectAnimator>
AnimatorSet - <set>

The following example plays the two sets of object animations sequentially, with the first nested set playing two object animations together:

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

In order to run this animation, we must inflate the XML resources in our code to an AnimatorSet object, and then set the target objects for all of the animations before starting the animation set. Calling setTarget() sets a single target object for all children of the AnimatorSet as a convenience.

(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply {
    setTarget(myObject)
    start()
}

We can also declare a ValueAnimator in XML, as shown in the following example:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

To use the previous ValueAnimator in our code, we must inflate the object, add an AnimatorUpdateListener, get the updated animation value, and use it in a property of one of our views, as shown in the following code:

(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply {
    addUpdateListener { updatedAnimation ->
        textView.translationX = updatedAnimation.animatedValue as Float
    }

    start()
}

That’s all about in this article.

Related Other Articles / Posts

Conclusion

In this article, we understood about Property Animation Overview in Android. This article described about Property Animation related concepts like Workflow, benefits, Animates View using ValueAnimator, ObjectAnimator and AnimatorSet in Android.

Thanks for reading! I hope you enjoyed and learned about Property Animation concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

React Native – How To Implement Animations Using The Animated API In React Native ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Implement Animations Using The Animated API In React Native ?).

In this article, We will learn how to implement animations using the animated api in react native platform. Animation is an important part of user experience design. It serves as feedback on user actions, informs users of system status, and guides them on how to interact with the interface.  This article demonstrates the practical implementation of Animations in React native application. The recommended way to animate in React Native for most cases is by using the Animated API.

A famous quote about learning is :

” He who laughs most, learns best. “


So Let’s begin.


Animation Methods

There are three main Animated methods that we can use to create animations:

  1. Animated.timing() — Maps time range to easing value.
  2. Animated.decay() — starts with an initial velocity and gradually slows to a stop.
  3. Animated.spring() — Simple single-spring physics model (Based on Rebound and Origami). Tracks velocity state to create fluid motions as the toValue updates, and can be chained together.

Along with these three Animated methods, there are three ways to call these animations along with calling them individually. We will be covering all three of these as well:

  1. Animated.parallel() — Starts an array of animations all at the same time.
  2. Animated.sequence() — Starts an array of animations in order, waiting for each to complete before starting the next. If the current running animation is stopped, no following animations will be started.
  3. Animated.stagger() — Array of animations may run in parallel (overlap), but are started in sequence with successive delays. Very similar to Animated.parallel() but allows us to add delays to the animations.


1. Animated.timing()


1.1 Animated.timing Basic Example

The first animation we will be creating is this spinning animation using Animated.timing().

// Example implementation:
Animated.timing(
  someValue,
  {
    toValue: number,
    duration: number,
    easing: easingFunction,
    delay: number
  }
)

This type of infinite animation can be useful to use when creating loading indicators, and is one of the more useful animations that we’ve used in many of React Native projects. This concept can also be used to create infinite animations of other types such as scaling up and down or some other type to indicate loading.

To get started, we need to either start with a new React Native project or with a blank existing React Native project. To get started with a new project, type react-native init and the name of the project in the folder in which we will be working in and then cd into that directory:

react-native init animations
cd animations

Now that we are in this folder, open the index.js  file. Now that we have a new project created, the first thing we will need to do is import AnimatedImage, and Easing from react-native below View that is already being imported:

import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Animated,
  Image,
  Easing
} from 'react-native'

Animated is the library we will be using to create the animations, and ships with React Native.

Image is needed so we can create an image in our UI.

Easing is a module that also ships with React Native. It allows us to use various predefined easing methods such as lineareasequadcubicsinelasticbouncebackbezierinoutinout, and others. We will be using linear as to have a consistent linear motion. We will have a better idea of how to implement them after this section is finished.

Next, we need to set an initial animated value for our spinning value. To do this, we will set the value in our constructor:

constructor () {
  super()
  this.spinValue = new Animated.Value(0)
}

We declare spinValue as a new Animated.Value and pass in 0 (zero). Next, we need to create a spin method and call this method on componentDidMount to get it going when the app loads:

componentDidMount () {
  this.spin()
}
spin () {
  this.spinValue.setValue(0)
  Animated.timing(
    this.spinValue,
    {
      toValue: 1,
      duration: 4000,
      easing: Easing.linear
    }
  ).start(() => this.spin())
}

Here, this spin() method does the following steps:

  1. Sets this.spinValue back to zero
  2. Calls the Animated.timing method and animates this.spinValue to a value of 1 with a duration of 4000 milliseconds and an easing of Easing.linearAnimated.timing takes two arguments, a value (this.spinValue) and a config object. This config object can take a toValue, a duration, an easing method, and a delay.
  3. We call start() on this Animated.timing method, and pass in a callback of this.spin which will be called when the animation is completed, basically creating an infinite animation. start() takes a completion callback that will be called when the animation is done. If the animation is done because it finished running normally, the completion callback will be invoked with {finished: true}, but if the animation is done because stop was called on it before it could finish (e.g. because it was interrupted by a gesture or another animation), then it will receive {finished: false}.

Now that our methods are set up, we need to render the animation in our UI. To do so, we need to update our render method:

render () {
  const spin = this.spinValue.interpolate({
    inputRange: [0, 1],
    outputRange: ['0deg', '360deg']
  })
  return (
    <View style={styles.container}>
      <Animated.Image
        style={{
          width: 227,
          height: 200,
          transform: [{rotate: spin}] }}
          source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/images/1198519/reactjs.png'}}
      />
    </View>
  )
}

Here, the render() method does the following steps:

  1. We create a variable named spin. In this variable we call interpolate() on this.spinValueinterpolate() is a method that is available to be called on any Animated.Value. interpolate is a method that interpolates the value before updating the property, e.g. mapping 0–1 to 0–10. So in our example, we need to map 0 (zero) degrees to 360 degrees numerically, using the numbers zero to one, and this method easily allows us to do this. We pass in an inputRange and outputRange array, and pass in [0,1] as the inputRange and [‘0deg’, ‘360deg’] as the outputRange.
  2. We return a View with a style of container, and an Animated.Image (React logo) with a heightwidth, and a transform property in which we attach our spin value to the rotate property, which is where the animation takes place:
transform: [{rotate: spin}]

Finally, we have the container style to center everything:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  }
})

That’s it, the animation should be working now!

The final code for this animation with a working example is :

index.js

import React, { Component } from 'react'
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Animated,
  Image,
  Easing
} from 'react-native'

const timing = 4000

class animations extends Component {
  constructor () {
    super()
    this.spinValue = new Animated.Value(0)
  }
  componentDidMount () {
    this.spin()
  }
  spin () {
    this.spinValue.setValue(0)
    Animated.timing(
      this.spinValue,
      {
        toValue: 1,
        duration: timing,
        easing: Easing.linear
      }
    ).start(() => this.spin())
  }
  render () {
    /* This also works, to show functions instead of strings */
    // const getStartValue = () => '0deg'
    // const getEndValue = () => '360deg'
    // const spin = this.spinValue.interpolate({
    //   inputRange: [0, 1],
    //   outputRange: [getStartValue(), getEndValue()]
    // })
    const spin = this.spinValue.interpolate({
      inputRange: [0, 1],
      outputRange: ['0deg', '360deg']
    })
    return (
      <View style={styles.container}>
        <Animated.Image
          style={{ width: 227, height: 200, transform: [{rotate: spin}] }}
          source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/images/1198519/reactjs.png'}}/>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  }
})

AppRegistry.registerComponent('animations', () => animations)

Note that – Use own image url for example code testing.

The output of the above example is :


1.2 Animated.timing multiple animation Example

Now that we know the basics of Animated.timing, let’s take a look at a few more examples of how to use Animated.timing along with interpolate and declare some other animations.

In the next example, we will declare a single animation value, this.animatedValue, and use the single animated value along with interpolate to create multiple animations, animating the following style properties:

  1. marginLeft
  2. opacity
  3. fontSize
  4. rotateX

To get started, either begin with a new branch or clear out our old code from the last project.

The first thing we will do is create the animated value that we will be using for these animations in the constructor:

constructor () {
  super()
  this.animatedValue = new Animated.Value(0)
}

Next, we create the animate method and call it in componentDidMount() :

componentDidMount () {
  this.animate()
}
animate () {
  this.animatedValue.setValue(0)
  Animated.timing(
    this.animatedValue,
    {
      toValue: 1,
      duration: 2000,
      easing: Easing.linear
    }
  ).start(() => this.animate())
}

In the render method, we create 5 different interpolated value variables:

render () { 
  const marginLeft = this.animatedValue.interpolate({
    inputRange: [0, 1],
    outputRange: [0, 300]
  })
  const opacity = this.animatedValue.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: [0, 1, 0]
  })
  const movingMargin = this.animatedValue.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: [0, 300, 0]
  })
  const textSize = this.animatedValue.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: [18, 32, 18]
  })
  const rotateX = this.animatedValue.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: ['0deg', '180deg', '0deg']
  })
...
}

interpolate is a very powerful method, allowing us to use this.animatedValue , a single animated value, in many ways. Because the value simply changes from zero to one, we are able to interpolate this property for styling opacity, margins, text sizes, and rotation properties.

We then return Animated.View and Animated.Text components implementing these new variables:

return (
    <View style={styles.container}>
      <Animated.View
        style={{
          marginLeft,
          height: 30,
          width: 40,
          backgroundColor: 'red'}} />
      <Animated.View
        style={{
          opacity,
          marginTop: 10,
          height: 30,
          width: 40,
          backgroundColor: 'blue'}} />
      <Animated.View
        style={{
          marginLeft: movingMargin,
          marginTop: 10,
          height: 30,
          width: 40,
          backgroundColor: 'orange'}} />
      <Animated.Text
        style={{
          fontSize: textSize,
          marginTop: 10,
          color: 'green'}} >
          Animated Text!
      </Animated.Text>
      <Animated.View
        style={{
          transform: [{rotateX}],
          marginTop: 50,
          height: 30,
          width: 40,
          backgroundColor: 'black'}}>
        <Text style={{color: 'white'}}>Hello from TransformX</Text>
      </Animated.View>
    </View>
)

We also update our container styling:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 150
  }
})

That’s it, the animation should be working now!

The final code for this animation with a working example is :

index.js

import React, { Component } from 'react'
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Animated,
  Image,
  Easing
} from 'react-native'

class animations extends Component {
  constructor () {
    super()
    this.animatedValue = new Animated.Value(0)
  }
  componentDidMount () {
    this.animate()
  }
  animate () {
    this.animatedValue.setValue(0)
    Animated.timing(
      this.animatedValue,
      {
        toValue: 1,
        duration: 2000,
        easing: Easing.linear
      }
    ).start(() => this.animate())
  }
  render () {
    const marginLeft = this.animatedValue.interpolate({
      inputRange: [0, 1],
      outputRange: [0, 300]
    })
    const opacity = this.animatedValue.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: [0, 1, 0]
    })
    const movingMargin = this.animatedValue.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: [0, 300, 0]
    })
    const textSize = this.animatedValue.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: [18, 32, 18]
    })
    const rotateX = this.animatedValue.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: ['0deg', '180deg', '0deg']
    })
    return (
      <View style={styles.container}>
        <Animated.View
          style={{
            marginLeft,
            height: 30,
            width: 40,
            backgroundColor: 'red'}} />
        <Animated.View
          style={{
            opacity,
            marginTop: 10,
            height: 30,
            width: 40,
            backgroundColor: 'blue'}} />
        <Animated.View
          style={{
            marginLeft: movingMargin,
            marginTop: 10,
            height: 30,
            width: 40,
            backgroundColor: 'orange'}} />
        <Animated.Text
          style={{
            fontSize: textSize,
            marginTop: 10,
            color: 'green'}} >
            Animated Text!
        </Animated.Text>
        <Animated.View
          style={{
            transform: [{rotateX}],
            marginTop: 50,
            height: 30,
            width: 40,
            backgroundColor: 'black'}}>
          <Text style={{color: 'white'}}>Hello from TransformX</Text>
        </Animated.View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 150
  }
})

AppRegistry.registerComponent('animations', () => animations)

The output of the above example is :


2. Animated.decay()

Decay used to animate the value from its initial velocity down to zero using the deceleration option.

state = {
  animation: new Animated.Value(-150)
}

componentDidMount() {
  Animated.decay(
    this.state.animation,
    {
      toValue: 200,
      duration: 2000,
      velocity: 0.95,
      deceleration: 0.998 // By default equals to 0.997
    }
  ).start();
}

To demonstrate that we are going to use Motion example. In this example, we are moving the box down the screen. Once the box reaches the finishing line at 200 points below the center, the decay effect applies. The box keeps moving, but its speed is slowing down until it stops.

The final code for this animation with a working example is :

import React from 'react';
import { Animated, View } from 'react-native';

export default class App extends React.Component {
  state = {
    animation: new Animated.Value(-150)
  }

  componentDidMount() {
    Animated.decay(
      this.state.animation,
      {
        toValue: 200,
        duration: 2000,
        velocity: 0.95,
        deceleration: 0.998
      }
    ).start();
  }

  render() {
    const animationStyles = {
      transform: [
        { translateY: this.state.animation }
      ]
    };

    return (
      <Animated.View style={[objectStyles.object, animationStyles]}>
      </Animated.View>
    );
  }
}

const objectStyles = {
  object: {
    backgroundColor: 'orange',
    width: 100,
    height: 100
  }
}

The output of the above example is :


3. Animated.spring()

Next, we will be creating an animation using the Animated.spring() method.

// Example implementation:
Animated.spring(
    someValue,
    {
      toValue: number,
      friction: number
    }
)

We can keep going from the same project, we just need to update a few things. In our constructor, let’s create a value called springValue and set it’s value to .3:

constructor () {
  super()
  this.springValue = new Animated.Value(0.3)
}

Next, let’s delete the animate() method and componentDidMount() method and create a new method called spring():

spring () {
  this.springValue.setValue(0.3)
  Animated.spring(
    this.springValue,
    {
      toValue: 1,
      friction: 1
    }
  ).start()
}

Here, this spring() method does the following steps:

  1. We set the springValue to .3 if it’s not already set to .3
  2. We call Animated.spring, passing in two arguments: a value to animate and a config object. The config object can take any of the following arguments: toValue (number), overshootClamping (boolean), restDisplacementThreshold (number), restSpeedThreshold (number), velocity (number), bounciness (number), speed (number), tension (number), and friction (number). The only required value is toValue, but friction and tension can help us get more control over the spring animation.
  3. We call start() to start the animation.

Now that the animation is set up, let’s attach the animation to a click event in our view, and the animation itself to the same React logo we used before:

<View style={styles.container}>
  <Text
    style={{marginBottom: 100}}
    onPress={this.spring.bind(this)}>Spring</Text>
    <Animated.Image
      style={{ width: 227, height: 200, transform: [{scale: this.springValue}] }}
      source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/images/1198519/reactjs.png'}}/>
</View>

Here, click event code do the following steps :

  1. We return a Text component attached to the spring() method to an onPress event.
  2. We return the Animated image and attach this.springValue to the scale property.

The spring animation should be working now!

The final code for this animation with a working example is :

import React, { Component } from 'react'
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Animated,
  Image
} from 'react-native'

class animations extends Component {
  constructor () {
    super()
    this.springValue = new Animated.Value(0.3)
  }
  spring () {
    this.springValue.setValue(0.3)
    Animated.spring(
      this.springValue,
      {
        toValue: 1,
        friction: 1,
        tension: 1
      }
    ).start()
  }
  render () {
    return (
      <View style={styles.container}>
        <Text style={{marginBottom: 100}} onPress={this.spring.bind(this)}>Spring</Text>
        <Animated.Image
          style={{ width: 227, height: 200, transform: [{scale: this.springValue}] }}
          source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/images/1198519/reactjs.png'}}/>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  }
})

AppRegistry.registerComponent('animations', () => animations)

Note that – Use own image url for example code testing.

The output of the above example is :


4. Animated.parallel()

Animated.parallel() starts an array of animations all at the same time.

Let’s take a look at the api and see how this works:

// API
Animated.parallel(arrayOfAnimations)
// In use:
Animated.parallel([
  Animated.spring(
    animatedValue,
    {
      //config options
    }
  ),
  Animated.timing(
     animatedValue2,
     {
       //config options
     }
  )
])

To get started, let’s go ahead and create the three animated values we will need in our constructor:

constructor () {
  super()
  this.animatedValue1 = new Animated.Value(0)
  this.animatedValue2 = new Animated.Value(0)
  this.animatedValue3 = new Animated.Value(0)
}

Next, we create our animate method and call it in componendDidMount() :

componentDidMount () {
  this.animate()
}
animate () {
  this.animatedValue1.setValue(0)
  this.animatedValue2.setValue(0)
  this.animatedValue3.setValue(0)
  const createAnimation = function (value, duration, easing, delay = 0) {
    return Animated.timing(
      value,
      {
        toValue: 1,
        duration,
        easing,
        delay
      }
    )
  }
  Animated.parallel([
    createAnimation(this.animatedValue1, 2000, Easing.ease),
    createAnimation(this.animatedValue2, 1000, Easing.ease, 1000),
    createAnimation(this.animatedValue3, 1000, Easing.ease, 2000)        
  ]).start()
}

In the animate method, we set the values of all three animated values back to zero. We then create a function called createAnimation() which returns a new animation , taking in the value, duration, easing, and delay as arguments. If no delay is passed in, we set it to zero.

We then call Animated.parallel() passing in the three animations we want to create using createAnimation().

In our render method, we next need to set up our interpolated values:

render () {
  const scaleText = this.animatedValue1.interpolate({
    inputRange: [0, 1],
    outputRange: [0.5, 2]
  })
  const spinText = this.animatedValue2.interpolate({
    inputRange: [0, 1],
    outputRange: ['0deg', '720deg']
  })
  const introButton = this.animatedValue3.interpolate({
    inputRange: [0, 1],
    outputRange: [-100, 400]
  })
  ...
}

Here, the render () method code do the following steps :

1. scaleText — We interpolate our values to be output as a range from 0.5 to 2, we will use this value to scale our text from .5 to 2.

2. spinText — We interpolate our values to be output as a range of 0 degrees to 720 degrees, essentially spinning the item two times.

3. introButton — We interpolate out values to be output as a range of -100 to 400, and will use this as a margin property in our View.

Finally, we render a main View with three Animated.Views:

<View style={[styles.container]}>
  <Animated.View 
    style={{ transform: [{scale: scaleText}] }}>
    <Text>Welcome</Text>
  </Animated.View>
  <Animated.View
    style={{ marginTop: 20, transform: [{rotate: spinText}] }}>
    <Text
      style={{fontSize: 20}}>
      to the App!
    </Text>
  </Animated.View>
  <Animated.View
    style={{top: introButton, position: 'absolute'}}>
    <TouchableHighlight
      onPress={this.animate.bind(this)}
      style={styles.button}>
      <Text
        style={{color: 'white', fontSize: 20}}>
        Click Here To Start
      </Text>
   </TouchableHighlight>
  </Animated.View>
</View>

We use scaleText to scale the first View, spinText to spin the second View, and introButton to animate the margin of the third View.

When animate() is called, all three animations run in parallel. The parallel animations should be working now!

The final code for this animation with a working example is :

import React, { Component } from 'react'
import {
  AppRegistry,
  StyleSheet,
  Text,
  Animated,
  View,
  Easing,
  TouchableHighlight,
  Dimensions
} from 'react-native'

const { width } = Dimensions.get('window')

class animations extends Component {

  constructor () {
    super()
    this.animatedValue1 = new Animated.Value(0)
    this.animatedValue2 = new Animated.Value(0)
    this.animatedValue3 = new Animated.Value(0)
  }

  componentDidMount () {
    this.animate()
  }

  animate () {
    this.animatedValue1.setValue(0)
    this.animatedValue2.setValue(0)
    this.animatedValue3.setValue(0)
    const createAnimation = function (value, duration, easing, delay = 0) {
      return Animated.timing(
       value,
        {
          toValue: 1,
          duration,
          easing,
          delay
        }
      )
    }
    Animated.parallel([
      createAnimation(this.animatedValue1, 2000, Easing.ease),
      createAnimation(this.animatedValue2, 1000, Easing.ease, 1000),
      createAnimation(this.animatedValue3, 1000, Easing.ease, 2000)
    ]).start()
  }

  render () {
    const scaleText = this.animatedValue1.interpolate({
      inputRange: [0, 1],
      outputRange: [0.5, 2]
    })
    const spinText = this.animatedValue2.interpolate({
      inputRange: [0, 1],
      outputRange: ['0deg', '720deg']
    })
    const introButton = this.animatedValue3.interpolate({
      inputRange: [0, 1],
      outputRange: [-100, 400]
    })
    return (
      <View style={[styles.container]}>
        <Animated.View style={{ transform: [{scale: scaleText}] }}>
          <Text>Welcome</Text>
        </Animated.View>
        <Animated.View style={{ marginTop: 20, transform: [{rotate: spinText}] }}>
          <Text style={{fontSize: 20}}>to the App!</Text>
        </Animated.View>
        <Animated.View style={{top: introButton, position: 'absolute'}}>
          <TouchableHighlight onPress={this.animate.bind(this)} style={styles.button}>
            <Text style={{color: 'white', fontSize: 20}}>Click Here To Start</Text>
          </TouchableHighlight>
        </Animated.View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  button: {
    width: width - 40,
    height: 70,
    marginLeft: 20,
    marginRight: 20,
    backgroundColor: 'blue',
    justifyContent: 'center',
    alignItems: 'center'
  }
})

AppRegistry.registerComponent('animations', () => animations)

The output of the above example is :


5. Animated.sequence()

Let’s take a look at the api and see how this animation works:

// API
Animated.sequence(arrayOfAnimations)
// In use
Animated.sequence([
  Animated.timing(
    animatedValue,
    {
      //config options
    }
  ),
  Animated.spring(
     animatedValue2,
     {
       //config options
     }
  )
])

Like Animated.parallel()Animated.sequence() takes an array of animations. Animated.sequence() runs an array of animations in order, waiting for each to complete before starting the next.

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Animated
} from 'react-native'

const arr = []
for (var i = 0; i < 500; i++) {
  arr.push(i)
}

class animations extends Component {

  constructor () {
    super()
    this.animatedValue = []
    arr.forEach((value) => {
      this.animatedValue[value] = new Animated.Value(0)
    })
  }

  componentDidMount () {
    this.animate()
  }

  animate () {
    const animations = arr.map((item) => {
      return Animated.timing(
        this.animatedValue[item],
        {
          toValue: 1,
          duration: 50
        }
      )
    })
    Animated.sequence(animations).start()
  }

  render () {
    const animations = arr.map((a, i) => {
      return <Animated.View key={i} style={{opacity: this.animatedValue[a], height: 20, width: 20, backgroundColor: 'red', marginLeft: 3, marginTop: 3}} />
    })
    return (
      <View style={styles.container}>
        {animations}
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'wrap'
  }
})

AppRegistry.registerComponent('animations', () => animations);

Because the apis for Animated.sequence() and Animated.parallel() are so similar, taking an array of animations, we are not going to repeat the walkthrough of each method.

The main thing to notice here that is different is that we are creating our Animated.Values with a loop since we are animating so many values. We are also rendering our Animated.Views with a map function returning a new Animated.View for each item in the array.

The sequence of animations should be working now! The output of the above example is :


6. Animated.stagger()

Let’s take a look at the api and see how this animation works:

// API
Animated.stagger(delay, arrayOfAnimations)
// In use:
Animated.stagger(1000, [
  Animated.timing(
    animatedValue,
    {
      //config options
    }
  ),
  Animated.spring(
     animatedValue2,
     {
       //config options
     }
  )
])

Like Animated.parallel() and Animated.sequence(), Animated.Stagger also takes an array of animations, but these animations are started in sequence with successive delays.

The main difference here is the first argument, the delay that will be applied to each animation.

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Animated
} from 'react-native'

const arr = []
for (var i = 0; i < 500; i++) {
  arr.push(i)
}

class animations extends Component {

  constructor () {
    super()
    this.animatedValue = []
    arr.forEach((value) => {
      this.animatedValue[value] = new Animated.Value(0)
    })
  }

  componentDidMount () {
    this.animate()
  }

  animate () {
    const animations = arr.map((item) => {
      return Animated.timing(
        this.animatedValue[item],
        {
          toValue: 1,
          duration: 4000
        }
      )
    })
    Animated.stagger(10, animations).start()
  }

  render () {
    const animations = arr.map((a, i) => {
      return <Animated.View key={i} style={{opacity: this.animatedValue[a], height: 20, width: 20, backgroundColor: 'red', marginLeft: 3, marginTop: 3}} />
    })
    return (
      <View style={styles.container}>
        {animations}
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'wrap'
  }
})

AppRegistry.registerComponent('animations', () => animations);

The staggered animations should be working now! The output of above example is :

That’s all about in this article.


Conclusion

In this article, We understood about Animation Animation Animated API in React Native. Animation is an important part of user experience design. It serves as feedback on user actions, informs users of system status, and guides them on how to interact with the interface. We discussed how to implement Animations using Animated Api in React native with some example.

Thanks for reading ! I hope you enjoyed and learned about the Animation Animated API Concepts in React Native. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolmonkTechie as below link :

You can also follow official website and tutorials of React Native as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING!!???

React Native – Understanding Animations In React Native

Hello Readers, CoolMonkTechie heartily welcomes you in this article (Understanding Animations In React Native).

In this article, We will learn about Animations in React Native. Animations are very important to create a great user experience. Stationary objects must overcome inertia as they start moving. Objects in motion have momentum and rarely come to a stop immediately. Animations allow us to convey physically believable motion in our interface.

A famous quote about learning is :

” Develop a passion for learning. If you do, you will never cease to grow.”

So Let’s begin.


Animation Systems

React Native provides two complementary animation systems: 

  • Animated for granular and interactive control of specific values. It is a built-in API for animating component styles.
  • LayoutAnimation for animated global layout transactions. It is a built-in API for animating between 2 component hierarchies (think “magic move”), although still considered experimental.

Most animations will use Animated.


1. Animated

The Animated API is designed to concisely express a wide variety of interesting animation and interaction patterns in a very performant way. Animated focuses on declarative relationships between inputs and outputs, with configurable transforms in between, and start/stop methods to control time-based animation execution.

Animated exports six animatable component types: ViewTextImageScrollViewFlatList and SectionList, but we can also create own using Animated.createAnimatedComponent().

The Animated API lets us animate component styles.

For example, a container view that fades in when it is mounted may look like this:

import React, { useRef, useEffect } from 'react';
import { Animated, Text, View } from 'react-native';

const FadeInView = (props) => {
  const fadeAnim = useRef(new Animated.Value(0)).current  // Initial value for opacity: 0

  React.useEffect(() => {
    Animated.timing(
      fadeAnim,
      {
        toValue: 1,
        duration: 10000,
      }
    ).start();
  }, [fadeAnim])

  return (
    <Animated.View                 // Special animatable View
      style={{
        ...props.style,
        opacity: fadeAnim,         // Bind opacity to animated value
      }}
    >
      {props.children}
    </Animated.View>
  );
}

// You can then use your `FadeInView` in place of a `View` in your components:
export default () => {
  return (
    <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
      <FadeInView style={{width: 250, height: 50, backgroundColor: 'powderblue'}}>
        <Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>Fading in</Text>
      </FadeInView>
    </View>
  )
}

Let’s break down what’s happening here.  In the FadeInView constructor, a new Animated.Value called fadeAnim is initialized as part of state. The opacity property on the View is mapped to this animated value. Behind the scenes, the numeric value is extracted and used to set opacity.

When the component mounts, the opacity is set to 0. Then, an easing animation is started on the fadeAnim animated value, which will update all of its dependent mappings (in this case, only the opacity) on each frame as the value animates to the final value of 1.

This is done in an optimized way that is faster than calling setState and re-rendering. Because the entire configuration is declarative, we will be able to implement further optimizations that serialize the configuration and runs the animation on a high-priority thread.


Configuring animations

Animations are heavily configurable. Custom and predefined easing functions, delays, durations, decay factors, spring constants, and more can all be tweaked depending on the type of animation.

Animated provides several animation types, the most commonly used one being Animated.timing(). It supports animating a value over time using one of various predefined easing functions, or we can use own. Easing functions are typically used in animation to convey gradual acceleration and deceleration of objects.

By default, timing will use an easeInOut curve that conveys gradual acceleration to full speed and concludes by gradually decelerating to a stop. We can specify a different easing function by passing an easing parameter. Custom duration or even a delay before the animation starts is also supported.

For example, if we want to create a 2-second long animation of an object that slightly backs up before moving to its final position:

Animated.timing(this.state.xPosition, {
  toValue: 100,
  easing: Easing.back(),
  duration: 2000
}).start();


Composing animations

Animations can be combined and played in sequence or in parallel. Sequential animations can play immediately after the previous animation has finished, or they can start after a specified delay. The Animated API provides several methods, such as sequence() and delay(), each of which take an array of animations to execute and automatically calls start()/stop() as needed.

For example, the following animation coasts to a stop, then it springs back while twirling in parallel:

Animated.sequence([
  // decay, then spring to start and twirl
  Animated.decay(position, {
    // coast to a stop
    velocity: { x: gestureState.vx, y: gestureState.vy }, // velocity from gesture release
    deceleration: 0.997
  }),
  Animated.parallel([
    // after decay, in parallel:
    Animated.spring(position, {
      toValue: { x: 0, y: 0 } // return to start
    }),
    Animated.timing(twirl, {
      // and twirl
      toValue: 360
    })
  ])
]).start(); // start the sequence group

If one animation is stopped or interrupted, then all other animations in the group are also stopped. Animated.parallel has a stopTogether option that can be set to false to disable this.


Combining animated values

We can combine two animated values via addition, multiplication, division, or modulo to make a new animated value.

There are some cases where an animated value needs to invert another animated value for calculation. An example is inverting a scale (2x –> 0.5x):

const a = new Animated.Value(1);
const b = Animated.divide(1, a);

Animated.spring(a, {
  toValue: 2
}).start();


Interpolation

Each property can be run through an interpolation first. An interpolation maps input ranges to output ranges, typically using a linear interpolation but also supports easing functions. By default, it will extrapolate the curve beyond the ranges given, but we can also have it clamp the output value.

A basic mapping to convert a 0-1 range to a 0-100 range would be:

value.interpolate({
  inputRange: [0, 1],
  outputRange: [0, 100]
});

For example, we may want to think about our Animated.Value as going from 0 to 1, but animate the position from 150px to 0px and the opacity from 0 to 1. This can be done by modifying style from the example above like so:

style={{
    opacity: this.state.fadeAnim, // Binds directly
    transform: [{
      translateY: this.state.fadeAnim.interpolate({
        inputRange: [0, 1],
        outputRange: [150, 0]  // 0 : 150, 0.5 : 75, 1 : 0
      }),
    }],
  }}

interpolate() supports multiple range segments as well, which is handy for defining dead zones and other handy tricks. For example, to get a negation relationship at -300 that goes to 0 at -100, then back up to 1 at 0, and then back down to zero at 100 followed by a dead-zone that remains at 0 for everything beyond that, we could do:

value.interpolate({
  inputRange: [-300, -100, 0, 100, 101],
  outputRange: [300, 0, 1, 0, 0]
});

Which would map like so:

Input | Output
------|-------
  -400|    450
  -300|    300
  -200|    150
  -100|      0
   -50|    0.5
     0|      1
    50|    0.5
   100|      0
   101|      0
   200|      0

interpolate() also supports mapping to strings, allowing us to animate colors as well as values with units. For example, if we wanted to animate a rotation we could do:

value.interpolate({
  inputRange: [0, 360],
  outputRange: ['0deg', '360deg']
});

interpolate() also supports arbitrary easing functions, many of which are already implemented in the Easing module. interpolate() also has configurable behavior for extrapolating the outputRange. We can set the extrapolation by setting the extrapolateextrapolateLeft, or extrapolateRight options. The default value is extend but we can use clamp to prevent the output value from exceeding outputRange.


Tracking dynamic values

Animated values can also track other values by setting the toValue of an animation to another animated value instead of a plain number. For example, a “Chat Heads” animation like the one used by Messenger on Android could be implemented with a spring() pinned on another animated value, or with timing() and a duration of 0 for rigid tracking. They can also be composed with interpolations:

Animated.spring(follower, { toValue: leader }).start();
Animated.timing(opacity, {
  toValue: pan.x.interpolate({
    inputRange: [0, 300],
    outputRange: [1, 0]
  })
}).start();

The leader and follower animated values would be implemented using Animated.ValueXY()ValueXY is a handy way to deal with 2D interactions, such as panning or dragging. It is a basic wrapper that contains two Animated.Value instances and some helper functions that call through to them, making ValueXY a drop-in replacement for Value in many cases. It allows us to track both x and y values in the example above.


Tracking gestures

Gestures, like panning or scrolling, and other events can map directly to animated values using Animated.event. This is done with a structured map syntax so that values can be extracted from complex event objects. The first level is an array to allow mapping across multiple args, and that array contains nested objects.

For example, when working with horizontal scrolling gestures, we would do the following in order to map event.nativeEvent.contentOffset.x to scrollX (an Animated.Value):

onScroll={Animated.event(
   // scrollX = e.nativeEvent.contentOffset.x
   [{ nativeEvent: {
        contentOffset: {
          x: scrollX
        }
      }
    }]
 )}

The following example implements a horizontal scrolling carousel where the scroll position indicators are animated using the Animated.event used in the ScrollView.

import React, { useRef } from "react";
import {
  SafeAreaView,
  ScrollView,
  Text,
  StyleSheet,
  View,
  ImageBackground,
  Animated,
  useWindowDimensions
} from "react-native";

const images = new Array(6).fill('https://images.unsplash.com/photo-1556740749-887f6717d7e4');

const App = () => {
  const scrollX = useRef(new Animated.Value(0)).current;

  const { width: windowWidth } = useWindowDimensions();

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.scrollContainer}>
        <ScrollView
          horizontal={true}
          style={styles.scrollViewStyle}
          pagingEnabled
          showsHorizontalScrollIndicator={false}
          onScroll={Animated.event([
            {
              nativeEvent: {
                contentOffset: {
                  x: scrollX
                }
              }
            }
          ])}
          scrollEventThrottle={1}
        >
          {images.map((image, imageIndex) => {
            return (
              <View
                style={{ width: windowWidth, height: 250 }}
                key={imageIndex}
              >
                <ImageBackground source={{ uri: image }} style={styles.card}>
                  <View style={styles.textContainer}>
                    <Text style={styles.infoText}>
                      {"Image - " + imageIndex}
                    </Text>
                  </View>
                </ImageBackground>
              </View>
            );
          })}
        </ScrollView>
        <View style={styles.indicatorContainer}>
          {images.map((image, imageIndex) => {
            const width = scrollX.interpolate({
              inputRange: [
                windowWidth * (imageIndex - 1),
                windowWidth * imageIndex,
                windowWidth * (imageIndex + 1)
              ],
              outputRange: [8, 16, 8],
              extrapolate: "clamp"
            });
            return (
              <Animated.View
                key={imageIndex}
                style={[styles.normalDot, { width }]}
              />
            );
          })}
        </View>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center"
  },
  scrollContainer: {
    height: 300,
    alignItems: "center",
    justifyContent: "center"
  },
  card: {
    flex: 1,
    marginVertical: 4,
    marginHorizontal: 16,
    borderRadius: 5,
    overflow: "hidden",
    alignItems: "center",
    justifyContent: "center"
  },
  textContainer: {
    backgroundColor: "rgba(0,0,0, 0.7)",
    paddingHorizontal: 24,
    paddingVertical: 8,
    borderRadius: 5
  },
  infoText: {
    color: "white",
    fontSize: 16,
    fontWeight: "bold"
  },
  normalDot: {
    height: 8,
    width: 8,
    borderRadius: 4,
    backgroundColor: "silver",
    marginHorizontal: 4
  },
  indicatorContainer: {
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "center"
  }
});

export default App;

The output of the above example is :

When using PanResponder, we could use the following code to extract the x and y positions from gestureState.dx and gestureState.dy. We use a null in the first position of the array, as we are only interested in the second argument passed to the PanResponder handler, which is the gestureState.

onPanResponderMove={Animated.event(
  [null, // ignore the native event
  // extract dx and dy from gestureState
  // like 'pan.x = gestureState.dx, pan.y = gestureState.dy'
  {dx: pan.x, dy: pan.y}
])}


Responding to the current animation value

We may notice that there is no clear way to read the current value while animating. This is because the value may only be known in the native runtime due to optimizations. If we need to run JavaScript in response to the current value, there are two approaches:

  • spring.stopAnimation(callback) will stop the animation and invoke callback with the final value. This is useful when making gesture transitions.
  • spring.addListener(callback) will invoke callback asynchronously while the animation is running, providing a recent value. This is useful for triggering state changes, for example snapping a bobble to a new option as the user drags it closer, because these larger state changes are less sensitive to a few frames of lag compared to continuous gestures like panning which need to run at 60 fps.

Animated is designed to be fully serializable so that animations can be run in a high performance way, independent of the normal JavaScript event loop. This does influence the API, so keep that in mind when it seems a little trickier to do something compared to a fully synchronous system. We need to check out Animated.Value.addListener as a way to work around some of these limitations, but use it sparingly since it might have performance implications in the future.


Using the native driver

The Animated API is designed to be serializable. By using the native driver, we send everything about the animation to native before starting the animation, allowing native code to perform the animation on the UI thread without having to go through the bridge on every frame. Once the animation has started, the JS thread can be blocked without affecting the animation.

Using the native driver for normal animations is straightforward. We can add useNativeDriver: true to the animation config when starting it.

Animated.timing(this.state.animatedValue, {
  toValue: 1,
  duration: 500,
  useNativeDriver: true // <-- Add this
}).start();

Animated values are only compatible with one driver so if we use native driver when starting an animation on a value, make sure every animation on that value also uses the native driver.

The native driver also works with Animated.event. This is especially useful for animations that follow the scroll position as without the native driver, the animation will always run a frame behind the gesture due to the async nature of React Native.

<Animated.ScrollView // <-- Use the Animated ScrollView wrapper
  scrollEventThrottle={1} // <-- Use 1 here to make sure no events are ever missed
  onScroll={Animated.event(
    [
      {
        nativeEvent: {
          contentOffset: { y: this.state.animatedValue }
        }
      }
    ],
    { useNativeDriver: true } // <-- Add this
  )}>
  {content}
</Animated.ScrollView>

Not everything we can do with Animated is currently supported by the native driver. The main limitation is that we can only animate non- layout properties: things like transform and opacity will work, but Flexbox and position properties will not. When using Animated.event, it will only work with direct events and not bubbling events. This means it does not work with PanResponder but does work with things like ScrollView#onScroll.

When an animation is running, it can prevent VirtualizedList components from rendering more rows. If we need to run a long or looping animation while the user is scrolling through a list, we can use isInteraction: false in our animation’s config to prevent this issue.


2. LayoutAnimation API

LayoutAnimation allows us to globally configure create and update animations that will be used for all views in the next render/layout cycle. This is useful for doing Flexbox layout updates without bothering to measure or calculate specific properties in order to animate them directly, and is especially useful when layout changes may affect ancestors, for example a “see more” expansion that also increases the size of the parent and pushes down the row below which would otherwise require explicit coordination between the components in order to animate them all in sync.

Note that although LayoutAnimation is very powerful and can be quite useful, it provides much less control than Animated and other animation libraries, so we may need to use another approach if we can’t get LayoutAnimation to do what you want.

Note that in order to get this to work on Android, we need to set the following flags via UIManager:

UIManager.setLayoutAnimationEnabledExperimental &&
  UIManager.setLayoutAnimationEnabledExperimental(true);

This example uses a preset value, we can customize the animations as we need,

import React from 'react';
import {
  NativeModules,
  LayoutAnimation,
  Text,
  TouchableOpacity,
  StyleSheet,
  View,
} from 'react-native';

const { UIManager } = NativeModules;

UIManager.setLayoutAnimationEnabledExperimental &&
  UIManager.setLayoutAnimationEnabledExperimental(true);

export default class App extends React.Component {
  state = {
    w: 100,
    h: 100,
  };

  _onPress = () => {
    // Animate the update
    LayoutAnimation.spring();
    this.setState({ w: this.state.w + 15, h: this.state.h + 15 })
  }

  render() {
    return (
      <View style={styles.container}>
        <View style={[styles.box, { width: this.state.w, height: this.state.h }]} />
        <TouchableOpacity onPress={this._onPress}>
          <View style={styles.button}>
            <Text style={styles.buttonText}>Press me!</Text>
          </View>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  box: {
    width: 200,
    height: 200,
    backgroundColor: 'red',
  },
  button: {
    backgroundColor: 'black',
    paddingHorizontal: 20,
    paddingVertical: 15,
    marginTop: 15,
  },
  buttonText: {
    color: '#fff',
    fontWeight: 'bold',
  },
});

The output of the above example is :

That’s all about in this article.

Related Other Articles / Posts


Conclusion

In this article, We understood about Animations in React Native. Animation is an important part of user experience design. It serves as feedback on user actions, informs users of system status, and guides them on how to interact with the interface. 

Thanks for reading ! I hope you enjoyed and learned about the Animations Concepts in React Native. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolmonkTechie as below link :

You can also follow official website and tutorials of React Native as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING!!???

Exit mobile version