React Native – How To Manage The Lifecycle of Gestures In React Native ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Manage The Lifecycle of Gestures In React Native ?).

In this article, We will learn how to manage the lifecycle of gestures in react native. The gesture responder system manages the lifecycle of gestures in react native application. A touch can go through several phases as the app determines what the user’s intention is. For example, the app needs to determine if the touch is scrolling, sliding on a widget, or tapping. This can even change during the duration of a touch. There can also be multiple simultaneous touches.

The touch responder system is needed to allow components to negotiate these touch interactions without any additional knowledge about their parent or child components.

A famous quote about learning is :

” Change is the end result of all true learning. “

So Let’s begin.


Best Practices

To make our app feel great, every action should have the following attributes:

  • Feedback/highlighting – show the user what is handling their touch, and what will happen when they release the gesture
  • Cancel-ability – when making an action, the user should be able to abort it mid-touch by dragging their finger away

These features make users more comfortable while using an app, because it allows people to experiment and interact without fear of making mistakes.


TouchableHighlight and Touchable

The responder system can be complicated to use. So we have provided an abstract Touchable implementation for things that should be “tappable“. This uses the responder system and allows us to configure tap interactions declaratively. Use TouchableHighlight anywhere where we would use a button or link on web.


Gesture Responder System Lifecycle

A view can become the touch responder by implementing the correct negotiation methods. There are two methods to ask the view if it wants to become responder:

  • View.props.onStartShouldSetResponder: (evt) => true, – Does this view want to become responder on the start of a touch?
  • View.props.onMoveShouldSetResponder: (evt) => true, – Called for every touch move on the View when it is not the responder: does this view want to “claim” touch responsiveness?

If the View returns true and attempts to become the responder, one of the following will happen:

  • View.props.onResponderGrant: (evt) => {} – The View is now responding for touch events. This is the time to highlight and show the user what is happening.
  • View.props.onResponderReject: (evt) => {} – Something else is the responder right now and will not release it.

If the view is responding, the following handlers can be called:

  • View.props.onResponderMove: (evt) => {} – The user is moving their finger.
  • View.props.onResponderRelease: (evt) => {} – Fired at the end of the touch, ie “touchUp”.
  • View.props.onResponderTerminationRequest: (evt) => true – Something else wants to become responder. Should this view release the responder? Returning true allows release.
  • View.props.onResponderTerminate: (evt) => {} – The responder has been taken from the View. Might be taken by other views after a call to onResponderTerminationRequest, or might be taken by the OS without asking (happens with control center/ notification center on iOS).

evt is a synthetic touch event with the following nativeEvent form:

  • changedTouches – Array of all touch events that have changed since the last event
  • identifier – The ID of the touch
  • locationX – The X position of the touch, relative to the element
  • locationY – The Y position of the touch, relative to the element
  • pageX – The X position of the touch, relative to the root element
  • pageY – The Y position of the touch, relative to the root element
  • target – The node id of the element receiving the touch event
  • timestamp – A time identifier for the touch, useful for velocity calculation
  • touches – Array of all current touches on the screen


Capture ShouldSet Handlers

onStartShouldSetResponder and onMoveShouldSetResponder are called with a bubbling pattern, where the deepest node is called first. That means that the deepest component will become responder when multiple Views return true for ShouldSetResponder handlers. This is desirable in most cases, because it makes sure all controls and buttons are usable.

However, sometimes a parent will want to make sure that it becomes responder. This can be handled by using the capture phase. Before the responder system bubbles up from the deepest component, it will do a capture phase, firing on ShouldSetResponderCapture. So if a parent View wants to prevent the child from becoming responder on a touch start, it should have a onStartShouldSetResponderCapture handler which returns true.

  • View.props.onStartShouldSetResponderCapture: (evt) => true,
  • View.props.onMoveShouldSetResponderCapture: (evt) => true,


Pan Responder

PanResponder reconciles several touches into a single gesture. It makes single-touch gestures resilient to extra touches, and can be used to recognize basic multi-touch gestures.

By default, PanResponder holds an InteractionManager handle to block long-running JS events from interrupting active gestures.

It provides a predictable wrapper of the responder handlers provided by the gesture responder system. For each handler, it provides a new gestureState object alongside the native event object:

onPanResponderMove: (event, gestureState) => {}

A native event is a synthetic touch event with form of PressEvent.

gestureState object has the following:

  • stateID – ID of the gestureState- persisted as long as there at least one touch on screen
  • moveX – the latest screen coordinates of the recently-moved touch
  • moveY – the latest screen coordinates of the recently-moved touch
  • x0 – the screen coordinates of the responder grant
  • y0 – the screen coordinates of the responder grant
  • dx – accumulated distance of the gesture since the touch started
  • dy – accumulated distance of the gesture since the touch started
  • vx – current velocity of the gesture
  • vy – current velocity of the gesture
  • numberActiveTouches – Number of touches currently on screen


API

Only a single component can respond to touch events at one time – the component responding to events owns a global “interaction lock”. The PanResponder API helps us manage what component owns this lock through a set of callbacks. Each of these callbacks is also passed an event and gestureState object containing info about the touch events (e.g. position and velocity).

To create a PanResponder, we call PanResponder.create(callbacksObject). The result is a set of props that can be passed to View as props (these are the lower-level touch event handling props). We’ll typically wrap the result with useRef, since we only want to create a single PanResponder for the lifecycle of the component.

The full set of callbacks we can pass is:

  • onStartShouldSetPanResponder: (event, gestureState) => {}
  • onStartShouldSetPanResponderCapture: (event, gestureState) => {}
  • onMoveShouldSetPanResponder: (event, gestureState) => {}
  • onMoveShouldSetPanResponderCapture: (event, gestureState) => {}
  • onPanResponderReject: (event, gestureState) => {}
  • onPanResponderGrant: (event, gestureState) => {}
  • onPanResponderStart: (event, gestureState) => {}
  • onPanResponderEnd: (event, gestureState) => {}
  • onPanResponderRelease: (event, gestureState) => {}
  • onPanResponderMove: (event, gestureState) => {}
  • onPanResponderTerminate: (event, gestureState) => {}
  • onPanResponderTerminationRequest: (event, gestureState) => {}
  • onShouldBlockNativeResponder: (event, gestureState) => {}


Usage Pattern

const ExampleComponent = () => {
  const panResponder = React.useRef(
    PanResponder.create({
      // Ask to be the responder:
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) =>
        true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) =>
        true,

      onPanResponderGrant: (evt, gestureState) => {
        // The gesture has started. Show visual feedback so the user knows
        // what is happening!
        // gestureState.d{x,y} will be set to zero now
      },
      onPanResponderMove: (evt, gestureState) => {
        // The most recent move distance is gestureState.move{X,Y}
        // The accumulated gesture distance since becoming responder is
        // gestureState.d{x,y}
      },
      onPanResponderTerminationRequest: (evt, gestureState) =>
        true,
      onPanResponderRelease: (evt, gestureState) => {
        // The user has released all touches while this view is the
        // responder. This typically means a gesture has succeeded
      },
      onPanResponderTerminate: (evt, gestureState) => {
        // Another component has become the responder, so this gesture
        // should be cancelled
      },
      onShouldBlockNativeResponder: (evt, gestureState) => {
        // Returns whether this component should block native components from becoming the JS
        // responder. Returns true by default. Is currently only supported on android.
        return true;
      }
    })
  ).current;

  return <View {...panResponder.panHandlers} />;
};


Example

PanResponder works with Animated API to help build complex gestures in the UI. The following example contains an animated View component which can be dragged freely across the screen.

import React, { useRef } from "react";
import { Animated, View, StyleSheet, PanResponder, Text } from "react-native";

const App = () => {
  const pan = useRef(new Animated.ValueXY()).current;

  const panResponder = useRef(
    PanResponder.create({
      onMoveShouldSetPanResponder: () => true,
      onPanResponderGrant: () => {
        pan.setOffset({
          x: pan.x._value,
          y: pan.y._value
        });
      },
      onPanResponderMove: Animated.event(
        [
          null,
          { dx: pan.x, dy: pan.y }
        ]
      ),
      onPanResponderRelease: () => {
        pan.flattenOffset();
      }
    })
  ).current;

  return (
    <View style={styles.container}>
      <Text style={styles.titleText}>Drag this box!</Text>
      <Animated.View
        style={{
          transform: [{ translateX: pan.x }, { translateY: pan.y }]
        }}
        {...panResponder.panHandlers}
      >
        <View style={styles.box} />
      </Animated.View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center"
  },
  titleText: {
    fontSize: 14,
    lineHeight: 24,
    fontWeight: "bold"
  },
  box: {
    height: 150,
    width: 150,
    backgroundColor: "blue",
    borderRadius: 5
  }
});

export default App;

The output of the above example is :

That’s all about in this article.


Conclusion

In this article, We understood how to manage the lifecycle of gestures in react native. We also discussed about Best Practices and PanResponser usage in React Native platform.

Thanks for reading ! I hope you enjoyed and learned about the Gestures 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 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 :

React Native Official Website

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 – How To Navigate Between Screens In React Native ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Navigate Between Screens In React Native ?).

In this article, We will learn how to navigate between screens in react native. Mobile apps are rarely made up of a single screen. Managing the presentation of, and transition between, multiple screens is typically handled by what is known as a navigator. This article covers the various navigation components available in React Native. If we are getting started with navigation, we will probably want to use React Navigation. React Navigation provides a straightforward navigation solution, with the ability to present common stack navigation and tabbed navigation patterns on both Android and iOS.

If we’d like to achieve a native look and feel on both Android and iOS, or we’re integrating React Native into an app that already manages navigation natively, the following library provides native navigation on both platforms: react-native-navigation.

A famous quote about learning is :

” Study hard what interests you the most in the most undisciplined, irreverent and original manner possible. “

So Let’s begin.


Overview

The community solution to navigation is a standalone library that allows developers to set up the screens of an app with a few lines of code.

React Navigation is the most popular navigation library. It handles most of the challenges described above nicely, and is sufficiently configurable for most apps.

React Navigation provides all the different type of navigator like

  • Stack Navigator : For the simple screen switching
  • Drawer Navigator : To create Navigation Drawer/ Sidebar
  • Bottom Tab Navigator : For the bottom navigation
  • Top Tab Navigator : To create the tab navigation


Navigation Challenges

There are a few aspects of navigation which make it challenging in React Native:

  • Navigation works differently on each native platform. iOS uses view controllers, while android uses activities. These platform-specific APIs work differently technically and appear differently to the user. React Native navigation libraries try to support the look-and-feel of each platform, while still providing a single consistent JavaScript API.
  • Native navigation APIs don’t correspond with “views”. React Native components like ViewText, and Image, roughly map to an underlying native “view”, but there isn’t really an equivalent for some of the navigation APIs. There isn’t always a clear way to expose these APIs to JavaScript.
  • Navigation on mobile is stateful. On the web, navigation is typically stateless, where a url (i.e. route) takes a user to a single screen/page. On mobile, the history of the user’s navigation state is persisted in the application so that the user can go back to previous screens – a stack of screens can even include the same screen multiple times.

Due to these challenges, there isn’t a single best way to implement navigation, so it was removed from the core React Native package.


Installation and Setup

First, we need to install them in our project:

npm install @react-navigation/native @react-navigation/stack

Next, we install the required peer dependencies. We need to run different commands depending on whether our project is an Expo managed project or a bare React Native project.

  • If we have an Expo managed project, install the dependencies with expo:
expo install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
  • If we have a bare React Native project, install the dependencies with npm:
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

For iOS with bare React Native project, make sure we have Cocoapods installed. Then we install the pods to complete the installation:

cd ios
pod install
cd ..

We might get warnings related to peer dependencies after installation. They are usually caused by incorrect version ranges specified in some packages. We can safely ignore most warnings as long as our app builds.

To finalize installation of react-native-gesture-handler, add the following at the top (make sure it’s at the top and there’s nothing else before it) of our entry file, such as index.js or App.js:

import 'react-native-gesture-handler';

Now, we need to wrap the whole app in NavigationContainer. Usually we’d do this in our entry file, such as index.js or App.js :

import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';

const App = () => {
  return (
    <NavigationContainer>
      {/* Rest of app code */}
    </NavigationContainer>
  );
};

export default App;

Now we are ready to build and run our app on the device/simulator.


Steps For Adding React Navigation To The App

In this section, we’ll walk through adding react-navigation to an app.

After install the required dependencies, we need to do 3 things:

  • Set up a <NavigationContainer> from @react-navigation/native
  • Create a navigator:
    • createStackNavigator from @react-navigation/stack
    • createBottomTabNavigator from @react-navigation/bottom-tabs
    • createDrawerNavigator from @react-navigation/drawer
  • Define the screens in our app


1. Create a navigator

We first choose one of the available navigators, e.g. stack, which will act as our root navigator. Navigators may be nested later.

import { createStackNavigator } from '@react-navigation/stack'

const Root = createStackNavigator()


2. Create screen components

Next, we create a component for each screen.

Screens are regular React components. They’ll be passed navigation-specific props when instantiated.

const Screen1 = ({ navigation, route }) => {
  return <Text>Screen1</Text>
}


3. Render it

Lastly, we render a NavigationContainer with our navigator within it.

Each Screen component defines a route in our app. If we want nested navigators, e.g. a tab navigator within a stack navigator, we can use another navigator as a screen’s component. We only need a single NavigationContainer, even if we have nested navigators.

import { NavigationContainer } from '@react-navigation/native'

// ...

const App = () => {
  return (
    <NavigationContainer>
      <Root.Navigator>
        <Root.Screen name="Screen1" component={Screen1} />
        <Root.Screen name="Screen2" component={Screen2} />
        <Root.Screen name="Screen3" component={Screen3} />
      </Root.Navigator>
    </NavigationContainer>
  )
}


Navigating and Routes

Each navigator supports different ways of navigating:

  • Stack: push
  • Tabs: navigate
  • Drawer: openDrawer

When navigating, we typically specify a screen name and optionally parameters, e.g. navigator.push("Screen2", { paramA: "Hello!" }).


Navigation

In this example, we navigate from Screen1 to Screen2 by pushing Screen2 onto the stack when a button is pressed.

const Screen1 = ({ navigation }) => {
  return (
    <Button
      onPress={() => {
        navigation.push('Screen2', { paramA: 'Hello!' })
      }}
    />
  )
}


Routes

Within a screen, we have access to the current route and it’s parameters.

const Screen2 = ({ route }) => {
  return <Text>{route.params.paramA}</Text>
}


Hooks

For components which aren’t screens (direct descendants of a navigator), we can access the navigation and route objects using hooks. Some developers prefer using these hooks instead of props, even in screen components.

import { useNavigation, useRoute } from '@react-navigation/native'

const Screen1 = () => {
  const navigation = useNavigation()
  const route = useRoute()

  // ...
}


Usage

Now we can create an app with a home screen and a profile screen:

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

const MyStack = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={{ title: 'Welcome' }}
        />
        <Stack.Screen name="Profile" component={ProfileScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

In this example, there are 2 screens (Home and Profile) defined using the Stack.Screen component. Similarly, we can define as many screens as we like.

We can set options such as the screen title for each screen in the options prop of Stack.Screen.

Each screen takes a component prop that is a React component. Those components receive a prop called navigation which has various methods to link to other screens. For example, we can use navigation.navigate to go to the Profile screen:

const HomeScreen = ({ navigation }) => {
  return (
    <Button
      title="Go to Jane's profile"
      onPress={() =>
        navigation.navigate('Profile', { name: 'Jane' })
      }
    />
  );
};
const ProfileScreen = () => {
  return <Text>This is Jane's profile</Text>;
};

The views in the stack navigator use native components and the Animated library to deliver 60fps animations that are run on the native thread. Plus, the animations and gestures can be customized.

React Navigation also has packages for different kind of navigators such as tabs and drawer. We can use them to implement various patterns in our app.

That’s all about in this article.


Conclusion

In this article, We understood how to navigate between screens in react native. We also discussed about navigation challenges, dependencies and navigation usage in React Native platform.

Thanks for reading ! I hope you enjoyed and learned about the Navigation 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 – 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!!???

React Native – How To Use Touchable Components In React Native ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (React Native – How To Use Touchable Components In React Native?).

In this article, we will understand about how to use Touchable Components in React Native. Users interact with mobile apps mainly through touch. They can use a combination of gestures, such as tapping on a button, scrolling a list, or zooming on a map. React Native provides components to handle all sorts of common gestures, as well as a comprehensive gesture responder system to allow for more advanced gesture recognition, but the one component we will most likely be interested in is the basic Button. This article demonstrates the kinds of touchable with examples in React Native.

A famous quote about learning is :

” Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young. The greatest thing in life is to keep your mind young. “

So Let’s begin.


Displaying a basic button

Button provides a basic button component that is rendered nicely on all platforms. The minimal example to display a button looks like this:

<Button
  onPress={() => {
    alert('You tapped the button!');
  }}
  title="Press Me"
/>

This will render a blue label on iOS, and a blue rounded rectangle with light text on Android. Pressing the button will call the “onPress” function, which in this case displays an alert popup. If we like, we can specify a “color” prop to change the color of our button.

We can see the Button component using the example below. We can select which platform our app is previewed in by clicking on the toggle in the bottom right, then click on “Tap to Play” to preview the app.

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

export default class ButtonBasics extends Component {
  _onPressButton() {
    alert('You tapped the button!')
  }

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.buttonContainer}>
          <Button
            onPress={this._onPressButton}
            title="Press Me"
          />
        </View>
        <View style={styles.buttonContainer}>
          <Button
            onPress={this._onPressButton}
            title="Press Me"
            color="#841584"
          />
        </View>
        <View style={styles.alternativeLayoutButtonContainer}>
          <Button
            onPress={this._onPressButton}
            title="This looks great!"
          />
          <Button
            onPress={this._onPressButton}
            title="OK!"
            color="#841584"
          />
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
   flex: 1,
   justifyContent: 'center',
  },
  buttonContainer: {
    margin: 20
  },
  alternativeLayoutButtonContainer: {
    margin: 20,
    flexDirection: 'row',
    justifyContent: 'space-between'
  }
});

The result of above example is displayed below :


Touchables

In React Native, most “buttons” are actually implemented using Touchable components. Like Button, these components support an onPress prop. However, unlike Button, these components support custom styling – essentially a Touchable is a View that can be pressed.

Touchables have a variety of other props, like onPressIn and onPressOut, which give us more control over the behavior of the button. We can use these props to run custom animations.


Kinds of Touchable

Although we can run custom animations, most of the time, we use one of 2 built-in animations: a fade in opacity, or a change of color. There are preconfigured touchable components for each of these: TouchableOpacity and TouchableHighlight.

Which “Touchable” component we use will depend on what kind of feedback we want to provide:

  • Generally, we can use TouchableHighlight anywhere we would use a button or link on web. The view’s background will be darkened when the user presses down on the button.
  • We may consider using TouchableNativeFeedback on Android to display ink surface reaction ripples that respond to the user’s touch.
  • TouchableOpacity can be used to provide feedback by reducing the opacity of the button, allowing the background to be seen through while the user is pressing down.
  • If we need to handle a tap gesture but we don’t want any feedback to be displayed, use TouchableWithoutFeedback.

In some cases, we may want to detect when a user presses and holds a view for a set amount of time. These long presses can be handled by passing a function to the onLongPress props of any of the “Touchable” components.


Example

Let’s see all of these in action:

import React, { Component } from 'react';
import { Platform, StyleSheet, Text, TouchableHighlight, TouchableOpacity, TouchableNativeFeedback, TouchableWithoutFeedback, View } from 'react-native';

export default class Touchables extends Component {
  _onPressButton() {
    alert('You tapped the button!')
  }

  _onLongPressButton() {
    alert('You long-pressed the button!')
  }

  render() {
    return (
      <View style={styles.container}>
        <TouchableHighlight onPress={this._onPressButton} underlayColor="white">
          <View style={styles.button}>
            <Text style={styles.buttonText}>TouchableHighlight</Text>
          </View>
        </TouchableHighlight>
        <TouchableOpacity onPress={this._onPressButton}>
          <View style={styles.button}>
            <Text style={styles.buttonText}>TouchableOpacity</Text>
          </View>
        </TouchableOpacity>
        <TouchableNativeFeedback
            onPress={this._onPressButton}
            background={Platform.OS === 'android' ? TouchableNativeFeedback.SelectableBackground() : ''}>
          <View style={styles.button}>
            <Text style={styles.buttonText}>TouchableNativeFeedback {Platform.OS !== 'android' ? '(Android only)' : ''}</Text>
          </View>
        </TouchableNativeFeedback>
        <TouchableWithoutFeedback
            onPress={this._onPressButton}
            >
          <View style={styles.button}>
            <Text style={styles.buttonText}>TouchableWithoutFeedback</Text>
          </View>
        </TouchableWithoutFeedback>
        <TouchableHighlight onPress={this._onPressButton} onLongPress={this._onLongPressButton} underlayColor="white">
          <View style={styles.button}>
            <Text style={styles.buttonText}>Touchable with Long Press</Text>
          </View>
        </TouchableHighlight>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    paddingTop: 60,
    alignItems: 'center'
  },
  button: {
    marginBottom: 30,
    width: 260,
    alignItems: 'center',
    backgroundColor: '#2196F3'
  },
  buttonText: {
    textAlign: 'center',
    padding: 20,
    color: 'white'
  }
});

The result of above example is displayed below :


Scrolling and swiping

Gestures commonly used on devices with touchable screens include swipes and pans. These allow the user to scroll through a list of items, or swipe through pages of content. 

That’s all about in this article.


Conclusion

In this article, We understood about how to use Touchable Components in React Native.

Thanks for reading ! I hope you enjoyed and learned about the Touchable Components 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