如今,大多数应用程序允许用户通过滑动、双击、捏住、长按等方式与关键组件互动。如果在你的移动应用中正确实施,手势可以为你的用户提供一个吸引人、自然和直观的体验。事实上,你可以用手势取代大多数可见的控制,如按钮和图标点击。
有几个包可以用来在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手势处理程序库带有很多有用的手势,包括。
PanGestureHandlerTapGestureHandlerLongPressGestureHandlerPinchGestureHandler
除了上述手势外,我们还将演示如何实现拉动刷新、可滑动和双击手势。
初始化一个新的世博会应用程序
我们将从初始化一个新的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)库,我们将使用PanGestureHandler 。PanGestureHandler 是一个连续的手势处理程序,当用户平移(拖动)一个元素时,会产生手势事件流。
要开始使用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 数组中添加translateX 和translateY 属性,以指定平移手势沿X和Y轴的平移。
<Animated.View
style={[
styles.square,
{
transform: [
{
translateX: this.translateX,
},
{
translateY: this.translateY,
},
],
},
]}
/>
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 ,我们可以实现单点和双点手势。让我们从单点手势开始,然后转到双点手势。
单点手势
使用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>
</>
);
}
双击
使用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 钩。
在一个组件中实现单点和双点手势
用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 将不会被调用。
可滑动手势
使用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 组件来包裹View 和Text 组件。这样一来,我们的组件就可以刷卡了。但在现实世界中,当用户向左或向右滑动时,你会希望有某种事件或函数被调用。
要在左边渲染容器,使用renderLeftActions props并传入一个组件。要在右边渲染容器,使用renderRightActions 道具。
向左滑动时,我们有delete 容器,向右滑动时,我们有bookmark 容器。renderLeftActions 和renderRightActions 道具使这成为可能。
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;
按住/长按手势
用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>
);
}
用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 方法被调用时,我们使用setrefreshing 将refreshing 状态设置为true 。
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博客上。