How to Simplify animations in React Native with Moti

Author

Frontend Developer | Passionate about Web & Serverless

How to Simplify animations in React Native with Moti

March 24, 2021

react-nativeanimation

How to Simplify animations in React Native with Moti

Implementing Animations in React Native usually becomes difficult even if it is just to move a button or an item or things like headers, etc. We have packages like Animated from React Native, Reanimated v1 and Reanimated v2 etc. constantly trying to improve and simplify animations in React Native. But they still seem to fall short when it comes to the simplicity of using them. We are still required to write a lot of boilerplate just to implement simple animations in React Native unlike with the web.

Now here is some good news. We might finally have a better way to achieve this. Let me walk you through it.

Why Moti ?

Moti is a universal animation package for React Native made by Fernando Rojo powered by Reanimated v2, which is one of the latest versions of the most popular animation library Reanimated.

Let us consider a really common use case, an Infinite spinning animation.This type of infinite animation can be useful when creating loading spinners etc and is one of the most useful animations. Now let me walk you through the process that would be involved in achieving the same with both Animated as well as Moti.

We will be using expo for initialising the React native project. Let’s get started by creating the project.

Initializing the project

# Create a project named motiApp. Select the "blank" template when prompted

expo init motiApp
# Navigate to the project directory

cd motiApp

Starting the development server

expo start

Now that we have a new project created, let us first look at how we would do animation using the Animated library which is shipped with React Native.

For that the first thing we will need to do is import Animated, Image, and Easing from react-native

import { StyleSheet, 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 implements common easing functions.

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

const [spinValue, setSpinValue] = React.useState(new Animated.Value(0))

Next, we need to create a spin method and call this method in useEffect to get it going when the app loads.

React.useEffect(() => {
  runAnimationFn()
}, [])

const runAnimationFn = () => {
  Animated.loop(
    Animated.timing(spinValue, {
      toValue: 1,
      duration: 3000,
      easing: Easing.linear,
    })
  ).start()
}

Animated.loop() - Loops a given animation continuously, so that each time it reaches the end, it resets and begins again from the start.

Animated.timing() - Animates a value along a timed easing curve. We will be using linear as to have a consistent linear motion.

Now that our methods are set up, we need to render the animation in our UI. To do so,we need to create a variable and hook it up to one or more style attributes of an animated component.

const spin = spinValue.interpolate({
  inputRange: [0, 1],
  outputRange: ["0deg", "360deg"],
})

return (
  <View style={styles.container}>
    <Animated.Image
      style={{
        width: 227,
        height: 200,
        transform: [{ rotate: spin }],
      }}
      source={require("./assets/reactjs.png")}
    />
  </View>
)

So the final code would look something like this.

import * as React from "react"
import { Text, View, StyleSheet, Animated, Easing } from "react-native"

export default function App() {
  const [spinValue, setSpinValue] = React.useState(new Animated.Value(0))

  React.useEffect(() => {
    runAnimationFn()
  }, [])

  const runAnimationFn = () => {
    Animated.loop(
      Animated.timing(spinValue, {
        toValue: 1,
        duration: 3000,
        easing: Easing.linear,
      })
    ).start()
  }

  const spin = spinValue.interpolate({
    inputRange: [0, 1],
    outputRange: ["0deg", "360deg"],
  })

  return (
    <View style={styles.container}>
      <Animated.Image
        style={{
          width: 227,
          height: 200,
          transform: [{ rotate: spin }],
        }}
        source={require("./assets/reactjs.png")}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#ecf0f1",
    padding: 8,
  },
})

Yeah, that’s a lot of code just to implement this animation.

Now lets see how we can achieve the same with Moti.

The Moti Way 😎

Let’s get started by installing moti and its dependency Reanimated v2.

Installation

Install Moti

npm install moti

Install Reanimated 2

Moti requires that you install react-native-reanimated. The lowest version of Reanimated that has been tested on is 2.0.0-rc.0

Please follow the Expo instructions for installing react-native-reanimated v2.

You’ll need at least Expo SDK 40.

npm install react-native-reanimated@2.0.0-rc.0

Next let’s import the Image component from Moti.

Currently, Moti exports typical React Native components.

import { View, Text, ScrollView, SafeAreaView, Image } from "moti"

Since this might conflict with the names of your React Native components, It’s recommended we import them with aliases and add the required props in order to animate the Image component.

import { Image as MotiImage } from "moti"

;<MotiImage
  style={{
    width: 227,
    height: 200,
  }}
  from={{
    rotate: "0deg",
  }}
  animate={{
    rotate: "360deg",
  }}
  transition={{
    loop: true,
    repeatReverse: false,
    type: "timing",
    duration: 3000,
  }}
  source={require("./assets/reactjs.png")}
/>

All moti components have a few useful props:

from - The initial animation styles when the component mounts. In this case we have initial value of rotate: 0deg

animate - Animates any value passed here. In this case we have to rotate till 360deg

transition - Take full control over your animation configuration. You can set options for your entire animation (such as type: ‘timing’), or set transition options per-style.

Inside the transition prop we can set a few values which are required for our infinite animation . We set loop: true for infinite animation and repeatReverse: false to avoid back and forth animation.

Here is the full code:

import React from "react"
import { StyleSheet, View } from "react-native"
import { Image as MotiImage } from "moti"

export default function App() {
  return (
    <View style={styles.container}>
      <MotiImage
        style={{
          width: 227,
          height: 200,
        }}
        from={{
          rotate: "0deg",
        }}
        animate={{
          rotate: "360deg",
        }}
        transition={{
          loop: true,
          repeatReverse: false,
          type: "timing",
          duration: 3000,
        }}
        source={require("./assets/reactjs.png")}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
})

Yeah, that’s significantly less code when compared to Animated.

Get the full code here.

For More Info

Antstack Blog Post
Antstack Blog Post

Are you planning to go to serverless? AntStack is a cloud computing service and consulting company primarily focusing on Serverless Computing. We help companies get up and running with serverless, and we’ll make sure that there are no limits.

Keep track of our socials and connect with us - LinkedIn