Java Script - React native

How to use Pinch to Zoom Gesture in React Native apps

The open-source library react-native-gesture-handler is a great way to add gestures to cross-platform React Native apps. Two of the main reasons I find this useful because it uses native support to handle gestures and the other reason being it is better at performing on each native platform than React Native’s built-in touch system Gesture Responder system.

Setting up react-native-gesture-handler

yarn add react-native-gesture-handler
OR
npm install react-native-gesture-handler

Go to following link to see installation process

https://www.npmjs.com/package/react-native-gesture-handler

For iOS users, navigate inside ios/ directory from the terminal and run pod install.

Everything is set up, all you have to do is run the build command again, such as for iOS: react-native run-ios and for Android: react-native run-android.

Set up App component to display an image

In this section, let us quickly set up the App component to display a placeholder image. You can use any image as a placeholder. Here is the snippet for the App.js file to get started.

import React from 'react'
import { Image, View, Dimensions } from 'react-native'

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

const App = () => {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Image
        source={{
          uri: 'picsum.photos/536/354'
        }}
        style={{
          width: width,
          height: 300
        }}
        resizeMode="contain"
      />
    </View>
  )
}

export default App

It uses the width of the device’s screen to calculate the width of the image using Dimensions from react-native. To run this demo for the first time build the app for the platform you are using:

  • for iOS, run: react-native run-ios
  • for Android, run: react-native run-android

Using dynamic Image component with Animated API

Animated.Image is going to serve the purpose of displaying an image as well as perform scale animations.

Animated API uses declarative relationships between input and output values. For single values, you can use Animated.Value(). It is required since it is going to be a style property initially.

Start by importing Animated from react-native and replace the Image with Animated.Image.

import { View, Dimensions, Animated } from 'react-native'

// in return statement
return (
  <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
    <Animated.Image
      source={{
        uri: 'https://miro.medium.com/max/1080/1*7SYuZvH2pZnM0H79V4ttPg.jpeg'
      }}
      style={{
        width: width,
        height: 300,
        transform: [{ scale: 1 }]
      }}
      resizeMode="contain"
    />
  </View>
)

Also, by mentioning the value of the scale to one, it is going to display the image as usual.

Now, wrap the Animated.Image with PinchGestureHandler. Ths wrapper component is going to have two props.

return (
  <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
    <PinchGestureHandler
      onGestureEvent={this.onZoomEvent}
      onHandlerStateChange={this.onZoomStateChange}
    >
      <Animated.Image
        source={{
          uri: 'https://miro.medium.com/max/1080/1*7SYuZvH2pZnM0H79V4ttPg.jpeg'
        }}
        style={{
          width: width,
          height: 300,
          transform: [{ scale: this.scale }]
        }}
        resizeMode="contain"
      />
    </PinchGestureHandler>
  </View>
)

Adding Animated event and state change handler

Let us define the onZoomEvent first, before the return statement. This event is going to be an Animated event. This way gestures can directly map to animated values. The animated value to be used here is scale.

Passing useNativeDriver as boolean true allows the animations to happen on the native thread instead of JavaScript thread. This helps with performance.

scale = new Animated.Value(1)

onZoomEvent = Animated.event(
  [
    {
      nativeEvent: { scale: this.scale }
    }
  ],
  {
    useNativeDriver: true
  }
)

Now define the handler method onZoomStateChange that handles the state change when the gesture is over. Each gesture handler has is assigned a state that changes when a new touch event occurs.

There are different possible states for every handler but for the current gesture handler, ACTIVE is used to check whether the event is still active or not. To access these states, the object is required to import from the library itself.

The Animated.spring on scale property has toValue set to 1 which is the initial scale value when the animation is done.

onZoomStateChange = event => {
  if (event.nativeEvent.oldState === State.ACTIVE) {
    Animated.spring(this.scale, {
      toValue: 1,
      useNativeDriver: true
    }).start()
  }
}
Visited 11 times, 1 visit(s) today

Leave a Reply

Your email address will not be published. Required fields are marked *