React本地手势处理程序。轻扫、长按等

2,002 阅读9分钟

如今,大多数应用程序允许用户通过滑动、双击、捏住、长按等方式与关键组件互动。如果在你的移动应用中正确实施,手势可以为你的用户提供一个吸引人、自然和直观的体验。事实上,你可以用手势取代大多数可见的控制,如按钮和图标点击。

有几个包可以用来在React Native应用程序中实现手势。最受欢迎和推荐的库是 [react-native-gesture-handler](https://www.npmjs.com/package/react-native-gesture-handler).这个库将特定平台(即Android和iOS)的本地触摸和手势暴露给React Native。

虽然React Native中的手势可以使用内置的手势响应系统来处理,但这种实现有一些局限性,因为它是在JavaScript线程上运行的。因此,每次执行手势事件时,它都会将数据通过React Native桥发送到界面,这可能导致性能不佳。

React Native手势处理程序使你能够在React Native中实现高性能的手势,因为它在本地线程中运行,并遵循平台特定的行为,这反过来又会导致更好的性能。

React Native手势处理程序库带有很多有用的手势,包括。

  • PanGestureHandler
  • TapGestureHandler
  • LongPressGestureHandler
  • PinchGestureHandler

除了上述手势外,我们还将演示如何实现拉动刷新、可滑动和双击手势。

初始化一个新的世博会应用程序

我们将从初始化一个新的Expo应用开始。如果你的机器上还没有安装Expo,请运行下面的命令。

# installing expo CLI globally
npm install --global expo-cli

要创建一个新的Expo应用,请在你的终端上运行以下命令

# Create a project named react-native-gestures

expo init react-native-gestures

导航到你的项目目录,运行以下命令来启动你的应用程序。

expo start

i ,在iOS模拟器中打开,或按a ,在Android模拟器或连接的设备中打开。确保你的模拟器或仿真器已经设置好了。

现在让我们开始在我们的React Native应用中实现和管理手势。

平移手势

为了在React Native中使用该库实现平移手势,我们将使用。 [react-native-gesture-handler](https://www.npmjs.com/package/react-native-gesture-handler)库,我们将使用PanGestureHandlerPanGestureHandler 是一个连续的手势处理程序,当用户平移(拖动)一个元素时,会产生手势事件流。

要开始使用PanGestureHandler ,我们必须从我们之前安装的react-native-gesture-handler 库中导入它。

import { PanGestureHandler } from 'react-native-gesture-handler';

接下来,我们需要用我们之前导入的PanGestureHandler 组件来包裹我们想要应用平移手势的元素。

// in your return block
<PanGestureHandler>
  <View style={styles.square}/>
</PanGestureHandler>

为了让东西移动,我们需要使用名为onGestureEvent 的基本手势处理程序道具,并向其传递一个回调函数。

让我们创建一个函数,稍后我们将把它传递给onGestureEvent

// dont't forget to import Animated from react native
onPanGestureEvent = Animated.event(
    [
      {
        nativeEvent: {
          translationX: this.translateX,
          translationY: this.translateY,
        },
      },
    ],
    { useNativeDriver: true }
  );

请注意,我们在这里使用的是一个类组件。

让我们把我们刚刚创建的函数传递给PanGestureHandler 组件中的onGestureEvent 道具。

<PanGestureHandler onGestureEvent={this.onPanGestureEvent}> </PanGestureHandler>

由于我们想让我们的View 组件成为动画,让我们用Animated.View 来代替它,并在我们的transform 数组中添加translateXtranslateY 属性,以指定平移手势沿X和Y轴的平移。

  <Animated.View
            style={[
              styles.square,
              {
                transform: [
                  {
                    translateX: this.translateX,
                  },
                  {
                    translateY: this.translateY,
                  },
                ],
              },
            ]}
          />

Pan Gesture

React Native中的平移手势。

下面是用React Native手势处理程序创建平移手势的完整代码。

import React, { Component } from 'react';
import { StatusBar } from 'expo-status-bar';
import { PanGestureHandler } from 'react-native-gesture-handler';
import { Animated, StyleSheet, Text } from 'react-native';
export default class PanGesture extends Component {
  translateX = new Animated.Value(0);
  translateY = new Animated.Value(0);
  onPanGestureEvent = Animated.event(
    [
      {
        nativeEvent: {
          translationX: this.translateX,
          translationY: this.translateY,
        },
      },
    ],
    { useNativeDriver: true }
  );
  render() {
    return (
      <>
        <Text>Pan Gesture Handler</Text>
        <PanGestureHandler onGestureEvent={this.onPanGestureEvent}>
          <Animated.View
            style={[
              styles.square,
              {
                transform: [
                  {
                    translateX: this.translateX,
                  },
                  {
                    translateY: this.translateY,
                  },
                ],
              },
            ]}
          />
        </PanGestureHandler>
        <StatusBar style="auto" />
      </>
    );
  }
}
const styles = StyleSheet.create({
  square: {
    width: 150,
    height: 150,
    backgroundColor: '#28b5b5',
    marginTop: 22,
  },
});

TapGestureHandler

通过TapGestureHandler ,我们可以实现单点和双点手势。让我们从单点手势开始,然后转到双点手势。

单点手势

Single Tap Gesture

使用React Native手势处理程序的单点手势TapGestureHandler

我们将使用TapGestureHandler 组件来实现单点手势。注意我们正在添加onHandlerStateChange 道具和传递一个函数。

import { View, StyleSheet, Text } from 'react-native';
import { TapGestureHandler, State } from 'react-native-gesture-handler';

export default function TapGesture() {

  const onSingleTapEvent = (event) => {
    if (event.nativeEvent.state === State.ACTIVE) {
      alert('Hey single tap!');
    }
  };

  return (
    <>
      <Text>Double and Single Tap Gesture Handler</Text>
      <TapGestureHandler
        onHandlerStateChange={onSingleTapEvent}
        waitFor={doubleTapRef}
      >
        <View style={styles.square} />
      </TapGestureHandler>
    </>
  );
}

双击

Double-tap Gesture

使用React Native Gesture Handler的TapGestureHandler 双击手势。

单点和双点手势实现之间的主要区别是numberOfTaps props,它被传递给TapGestureHandler 组件。注意我们将2 传递给numberOfTaps ,以指定我们要触发onHandlerStateChange 的事件回调。

import React, { useRef, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { TapGestureHandler, State } from 'react-native-gesture-handler';

export default function TapGesture() {

  const [likeColour, setLikeColour] = useState('#28b5b5');
  const doubleTapRef = useRef(null);

  const onDoubleTapEvent = (event) => {
    if (event.nativeEvent.state === State.ACTIVE) {
      likeColour === '#28b5b5'
        ? setLikeColour('red')
        : setLikeColour('#28b5b5');
    }
  };
  const styles = StyleSheet.create({
    square: {
      width: 150,
      height: 150,
      backgroundColor: likeColour,
      marginTop: 22,
      marginBottom: 22,
    },
  });
  return (
    <>
      <Text>Double and Single Tap Gesture Handler</Text>
        <TapGestureHandler
          ref={doubleTapRef}
          onHandlerStateChange={onDoubleTapEvent}
          numberOfTaps={2}
        >
          <View style={styles.square} />
      </TapGestureHandler>
    </>
  );
}

注意,我们在这里使用了一个功能组件,因为我们使用了useRef 钩。

在一个组件中实现单点和双点手势

Double- and Single-tap Gestures

TapGestureHandler ,用React Native Gesture Handler实现双击和单击手势。

通过TapGestureHandler ,我们可以实现一种手势,在单点时调用一个事件,在双点时调用另一个。这方面的一个很好的例子是LinkedIn的移动应用,单点打开帖子,双点喜欢这个帖子。

import React, { useRef, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { TapGestureHandler, State } from 'react-native-gesture-handler';

export default function TapGesture() {

  const [likeColour, setLikeColour] = useState('#28b5b5');
  const doubleTapRef = useRef(null);

  const onSingleTapEvent = (event) => {
    if (event.nativeEvent.state === State.ACTIVE) {
      alert('Hey single');
    }
  };

  const onDoubleTapEvent = (event) => {
    if (event.nativeEvent.state === State.ACTIVE) {
      likeColour === '#28b5b5'
        ? setLikeColour('red')
        : setLikeColour('#28b5b5');
    }
  };

  const styles = StyleSheet.create({
    square: {
      width: 150,
      height: 150,
      backgroundColor: likeColour,
      marginTop: 22,
      marginBottom: 22,
    },
  });

  return (
    <>
      <Text>Double and Single Tap Gesture Handler</Text>
      <TapGestureHandler
        onHandlerStateChange={onSingleTapEvent}
        waitFor={doubleTapRef}
      >
        <TapGestureHandler
          ref={doubleTapRef}
          onHandlerStateChange={onDoubleTapEvent}
          numberOfTaps={2}
        >
          <View style={styles.square} />
        </TapGestureHandler>
      </TapGestureHandler>
    </>
  );
}

为了使其发挥作用,我们需要将waitFor={doubleTapRef} 添加到单点TapGestureHandler 组件中。当doubleTapRef 是truthy时,ononSingleTapEvent 将不会被调用。

可滑动手势

Swipeable Gestures

使用React Native Gesture Handle的Swipeable 组件的可滑动手势。

为了演示如何实现可滑动手势,让我们创建一个项目列表,用户可以向右或向左滑动,某些事件或方法被调用。

让我们创建一个平面列表组件,并将我们的数据传递给data props。

<FlatList
          data={todoList}
          keyExtractor={(item) => item.id}
          renderItem={({ item }) => <ListItem {...item} />}
          ItemSeparatorComponent={() => <Separator />}
        />

注意,renderItem props正在返回一个ListItem 组件。这代表了我们的列表todo项目,如上面的演示中所示。

现在,让我们创建我们的ListItem 组件,并使其可滑动。

首先,从react-native-gesture-handler 包中导入Swipeable 组件。

import Swipeable from 'react-native-gesture-handler/Swipeable';

接下来,用我们之前导入的Swipeable 组件来包裹ViewText 组件。这样一来,我们的组件就可以刷卡了。但在现实世界中,当用户向左或向右滑动时,你会希望有某种事件或函数被调用。

要在左边渲染容器,使用renderLeftActions props并传入一个组件。要在右边渲染容器,使用renderRightActions 道具。

向左滑动时,我们有delete 容器,向右滑动时,我们有bookmark 容器。renderLeftActionsrenderRightActions 道具使这成为可能。

onSwipeableRightOpen 道具接受一个方法,当从右到左的滑动手势完成时,即当右边的动作面板被打开时,该方法被调用。onSwipeableLeftOpen 道具接受一个方法,该方法在左边的操作面板被打开时被调用。

const ListItem = ({ text }) => (
  <Swipeable
    renderLeftActions={LeftSwipeActions}
    renderRightActions={rightSwipeActions}
    onSwipeableRightOpen={swipeFromRightOpen}
    onSwipeableLeftOpen={swipeFromLeftOpen}
  >
    <View
      style={{
        paddingHorizontal: 30,
        paddingVertical: 20,
        backgroundColor: 'white',
      }}
    >
      <Text style={{ fontSize: 24 }} style={{ fontSize: 20 }}>
        {text}
      </Text>
    </View>
  </Swipeable>
);

下面是可滑动手势的完整代码。

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  View,
  Text,
  StatusBar,
  FlatList,
} from 'react-native';
import Swipeable from 'react-native-gesture-handler/Swipeable';
const todoList = [
  { id: '1', text: 'Learn JavaScript' },
  { id: '2', text: 'Learn React' },
  { id: '3', text: 'Learn TypeScript' },
];
const Separator = () => <View style={styles.itemSeparator} />;
const LeftSwipeActions = () => {
  return (
    <View
      style={{ flex: 1, backgroundColor: '#ccffbd', justifyContent: 'center' }}
    >
      <Text
        style={{
          color: '#40394a',
          paddingHorizontal: 10,
          fontWeight: '600',
          paddingHorizontal: 30,
          paddingVertical: 20,
        }}
      >
        Bookmark
      </Text>
    </View>
  );
};
const rightSwipeActions = () => {
  return (
    <View
      style={{
        backgroundColor: '#ff8303',
        justifyContent: 'center',
        alignItems: 'flex-end',
      }}
    >
      <Text
        style={{
          color: '#1b1a17',
          paddingHorizontal: 10,
          fontWeight: '600',
          paddingHorizontal: 30,
          paddingVertical: 20,
        }}
      >
        Delete
      </Text>
    </View>
  );
};
const swipeFromLeftOpen = () => {
  alert('Swipe from left');
};
const swipeFromRightOpen = () => {
  alert('Swipe from right');
};
const ListItem = ({ text }) => (
  <Swipeable
    renderLeftActions={LeftSwipeActions}
    renderRightActions={rightSwipeActions}
    onSwipeableRightOpen={swipeFromRightOpen}
    onSwipeableLeftOpen={swipeFromLeftOpen}
  >
    <View
      style={{
        paddingHorizontal: 30,
        paddingVertical: 20,
        backgroundColor: 'white',
      }}
    >
      <Text style={{ fontSize: 24 }} style={{ fontSize: 20 }}>
        {text}
      </Text>
    </View>
  </Swipeable>
);
const SwipeGesture = () => {
  return (
    <>
      <StatusBar />
      <SafeAreaView style={styles.container}>
        <Text style={{ textAlign: 'center', marginVertical: 20 }}>
          Swipe right or left
        </Text>
        <FlatList
          data={todoList}
          keyExtractor={(item) => item.id}
          renderItem={({ item }) => <ListItem {...item} />}
          ItemSeparatorComponent={() => <Separator />}
        />
      </SafeAreaView>
    </>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  itemSeparator: {
    flex: 1,
    height: 1,
    backgroundColor: '#444',
  },
});
export default SwipeGesture;

按住/长按手势

Hold Gesture

LongPressGestureHandler 长按/按住手势,使用React Native Gesture Handle。

使用React Native手势处理程序,实现长按/保持手势是很简单的。

基本上,我们需要用LongPressGestureHandler 来包装我们想要实现手势的React Native组件,该组件是从react-native-gesture-handler 中导入的,然后添加onHandlerStateChange 道具,当用户在组件上保持一定的时间时,会触发一个方法。为了指定长按的时间,我们传入minDurationMs ,它接受一个以毫秒为单位的数字。

下面是上面显示的演示的代码。

import React from 'react';
import { View, StyleSheet } from 'react-native';
import { LongPressGestureHandler, State } from 'react-native-gesture-handler';

export default function LongPressGesture() {
  const onLongPress = (event) => {
    if (event.nativeEvent.state === State.ACTIVE) {
      alert("I've been pressed for 800 milliseconds");
    }
  };

  return (
    <LongPressGestureHandler
      onHandlerStateChange={onLongPress}
      minDurationMs={800}
    >
      <View style={styles.box} />
    </LongPressGestureHandler>
  );
}
const styles = StyleSheet.create({
  box: {
    width: 150,
    height: 150,
    backgroundColor: '#28b5b5',
    marginTop: 22,
    marginBottom: 22,
  },
});

捏合变焦

就像名字所暗示的那样,这是一个连续的手势,它跟踪两个手指之间的距离,并使用该信息来缩放你的内容。

render() {
    return (
      <PinchGestureHandler
        onGestureEvent={this.onPinchGestureEvent}
        onHandlerStateChange={this.onPinchHandlerStateChange}
      >
        <Animated.View
          style={[
            styles.pinchableImage,
            {
              transform: [{ perspective: 1 }, { scale: this.scale }],
            },
          ]}
        ></Animated.View>
      </PinchGestureHandler>
    );
  }

Pinch-to-zoom Gesture

PinchGestureHandler ,用React Native Gesture Handle进行捏合缩放手势。

为了使其发挥作用,我们需要将onGestureEvent 道具添加到我们的PinchGestureHandler ,然后在我们的transform 数组中设置{ scale: this.scale } 对象。

import React, { Component } from 'react';
import { View, Image, StyleSheet, Animated } from 'react-native';
import { PinchGestureHandler, State } from 'react-native-gesture-handler';
export default class PinchToZoom extends Component {
  baseScale = new Animated.Value(1);
  pinchScale = new Animated.Value(1);
  scale = Animated.multiply(this.baseScale, this.pinchScale);
  lastScale = 1;
  onPinchGestureEvent = Animated.event(
    [{ nativeEvent: { scale: this.pinchScale } }],
    { useNativeDriver: true }
  );
  onPinchHandlerStateChange = (event) => {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this.lastScale *= event.nativeEvent.scale;
      this.baseScale.setValue(this.lastScale);
      this.pinchScale.setValue(1);
    }
  };
  render() {
    return (
      <PinchGestureHandler
        onGestureEvent={this.onPinchGestureEvent}
        onHandlerStateChange={this.onPinchHandlerStateChange}
      >
        <Animated.View
          style={[
            styles.pinchableImage,
            {
              transform: [{ perspective: 1 }, { scale: this.scale }],
            },
          ]}
        ></Animated.View>
      </PinchGestureHandler>
    );
  }
}
const styles = StyleSheet.create({
  pinchableImage: {
    width: 250,
    height: 250,
    backgroundColor: '#28b5b5',
    marginTop: 22,
    marginBottom: 22,
  },
});

拉动刷新

要在React Native中实现pull-to-refresh,你不需要一个外部库。只需添加onRefresh props,它接受FlatList 组件中的一个函数,同时将refreshing props 设置为一个布尔值。

// at the top of your component
  const [refreshing, setrefreshing] = useState(false);

<FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={(item) => item.id}
        refreshing={refreshing}
        onRefresh={onRefresh}
      />

默认情况下,我们将refreshing 设置为false ,然后,当onRefresh 方法被调用时,我们使用setrefreshingrefreshing 状态设置为true

Pull-to-refresh Gesture

React Native中的Pull-to-refresh手势演示。

下面是上述演示的完整代码。

import React, { useState } from 'react';
import {
  SafeAreaView,
  View,
  FlatList,
  StyleSheet,
  Text,
  StatusBar,
} from 'react-native';
const DATA = [
  {
    id: '1',
    title: 'Take coffee',
  },
  {
    id: '2',
    title: 'Write some code',
  },
  {
    id: '3',
    title: 'Take the test',
  },
  {
    id: '4',
    title: 'Excercise',
  },
];
const Item = ({ title }) => (
  <View style={styles.item}>
    <Text style={styles.title}>{title}</Text>
  </View>
);
export default function PullToRefresh() {
  const [refreshing, setrefreshing] = useState(false);
  const [data, setdata] = useState(DATA);
  const onRefresh = () => {
    setrefreshing(true);
    setTimeout(() => {
      setdata((data) => [
        ...data,
        {
          id: '57878',
          title: 'Take a walk in the park',
        },
      ]);
      setrefreshing(false);
    }, 2000);
  };
  const renderItem = ({ item }) => <Item title={item.title} />;
  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={(item) => item.id}
        refreshing={refreshing}
        onRefresh={onRefresh}
      />
    </SafeAreaView>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: StatusBar.currentHeight || 0,
  },
  item: {
    backgroundColor: '#fad586',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  title: {
    fontSize: 20,
  },
});

总结

在React Native中实现手势可以帮助改善用户体验,使你的应用程序给用户带来自然的感觉。

在本教程中,我们介绍了React Native应用中手势的实现和管理,包括可滑动、平移、双击和单击、捏合缩放等等。

这个演示的所有代码都可以在GitHub上找到。

The postReact Native Gesture Handler:刷屏、长按等功能首次出现在LogRocket博客上。