the red penguin
HOME ABOUT SITEMAP BLOG LOGIN

Gestures

Here’s an example of using gestures. Firstly we set up a blank app. Note that to follow the tutor I had to add line 2 importing React.

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

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

We need to import useRef from the React library.

import React, { useRef } from 'react';

Secondly, we need to import the Animated API from react-native

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

We are going to create a circle in the middle of the screen so we can animate this later. We get rid of our placeholder text and create a new view, stylising it to look like a circle. So we do this:

<View styles={styles.circle}></View>

and this in styles:

  circle: {
    width: 150,
    height: 150,
    borderRadius: 75,
    backgroundColor: "green"
  }

The next step is to animate this. We’re going to transform an animated view that is going to wrap our view.

First we need to store a variable using useRef. It’s going to store an X and Y with a default of 0,0 – the centre of the screen.

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

We now need to wrap the circle in an AnimatedView and apply the transformation to it. So later, when we change the value of pan, it will move the circle. We do this by applying transform as a style.

      <Animated.View
        style={{
          transform:[{translateX: pan.x}, {translateY: pan.y}]
        }}
      >
        <View style={styles.circle}></View>
      </Animated.View>

The next step is how to animate the circle based on the movement of our finger. So firstly we need to import PanResponder.

import { StyleSheet, Text, View, Animated, PanResponder } from 'react-native';

Next we need to define this.

  const PanResponder = useRef(
    PanResponder.create({
      
    })
  ).current;

This is the shell of our PanResponder. Next, we need to provide PanResponder pan handlers (?!?!) as props of the animated view. This is to create a connection between our PanResponder and what we’re going to do with our view.

{...panResponder.panHandlers}

That has sorted out the View, now we need to look at the panResponder. We need to declare so that once movement has begun, we can set the panResponder so that we can put in updates about its move. It’s quite simple (!) –

onMoveShouldSetPanResponder: () => true,

The next step is that when the response has been granted we need to update our offset values.

      onPanResponderGrant: () => {
        pan.setOffset({
          x: pan.x._value,
          y: pan.y._value
        })
      }

When we are told that panResponder has detected a move, we need to use an animated event to move to the new X and Y values based on our pan variable.

      onPanResponderMove: Animated.event(
        [
          null,
          {dx: pan.x, dy:pan.y}
        ]
      )

Once the gesture has finished (touch has been removed from the screen) we have to reset everything.

      onPanResponderRelease: () => {
        pan.flattenOffset();
      }

Now we can click the circle and drag it around the screen!

What if we also want the circle to snap back to the centre of the screen when we let go? This is simple too.

When the gesture is released we need to reset the coordinates to 0,0, preferably with a bit of animation. So we can change onPanResponderRelease to the following:

      onPanResponderRelease: () => {
        Animated.spring(
          pan,
          {toValue:{x:0, y:0}}
        ).start();
      }

Now the circle nicely springs back when we release.

Here’s the full code.

import { StatusBar } from 'expo-status-bar';
import React, { useReducer, useRef } from 'react';
import { StyleSheet, Text, View, Animated, PanResponder } from 'react-native';

export default function 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: () => {
        Animated.spring(
          pan,
          {toValue:{x:0, y:0}}
        ).start();
      }
    })
  ).current;

  return (
    <View style={styles.container}>
      
      <StatusBar style="auto" />

      <Animated.View
        style={{
          transform:[{translateX: pan.x}, {translateY: pan.y}]
        }}
        {...panResponder.panHandlers}
      >
        <View style={styles.circle}></View>
      </Animated.View>

    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  circle: {
    width: 150,
    height: 150,
    borderRadius: 75,
    backgroundColor: "blue"
  }
});

Snack

Monday 30 January 2023, 112 views


Leave a Reply

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