React Native使用PanGestureHandler实现可拖拽底部抽屉上滑失效求解
问题描述
我是React Native动画的初学者,正在尝试开发自定义底部抽屉组件。我已经实现了两个动画函数,用于触发transform的translateY位置变更。我创建了两个独立的按钮来调用这两个函数,点击按钮时目标动画视图可以正常上下移动。我希望为该视图增加拖拽能力:向上拖拽时视图上滑,向下拖拽时视图下滑。为此我使用了react-native-gesture-handler提供的PanGestureHandler来处理手势动作。但我在手势处理函数中编写的逻辑存在问题,现在向下拖拽时视图可以正常下滑,但向上拖拽时视图无法上滑。
现有代码
import React, { useState } from "react"; import { StyleSheet, Text, View, Dimensions, useWindowDimensions, SafeAreaView, Animated, Button, } from "react-native"; import MapView from "react-native-maps"; import { PanGestureHandler, PanGestureHandlerGestureEvent, TouchableOpacity, } from "react-native-gesture-handler"; type AnimatedGHContext = { startX: number; startY: number; }; const { width } = Dimensions.get("screen"); const initialRegion = { latitudeDelta: 15, longitudeDelta: 15, latitude: 60.1098678, longitude: 24.7385084, }; export default function App() { const { height } = useWindowDimensions(); const [translateY] = useState(new Animated.Value(0)); const bringUpActionSheet = () => { Animated.timing(translateY, { toValue: 0, duration: 500, useNativeDriver: true, }).start(); }; const closeDownBottomSheet = () => { Animated.timing(translateY, { toValue: 1, duration: 500, useNativeDriver: true, }).start(); }; const bottomSheetIntropolate = translateY.interpolate({ inputRange: [0, 1], outputRange: [-height / 2.4 + 50, 0], }); const animatedStyle = { transform: [ { translateY: bottomSheetIntropolate, }, ], }; const gestureHandler = (e) => { console.log(e.nativeEvent.state); if (e.nativeEvent.state) { closeDownBottomSheet(); } else if (e.nativeEvent.state < 4) { bringUpActionSheet(); } }; return ( <View style={{ flex: 1 }}> <MapView style={styles.mapStyle} initialRegion={initialRegion} /> <PanGestureHandler onGestureEvent={gestureHandler}> <Animated.View style={[styles.container, { top: height * 0.7 }, animatedStyle]} > <Animated.View style={styles.grbber} /> <SafeAreaView style={styles.wrapper}> <View style={styles.content}> <Text style={styles.title}>I am scroll sheet</Text> <TouchableOpacity style={styles.button} onPress={() => closeDownBottomSheet()} > <Text style={styles.title}>Close</Text> </TouchableOpacity> <TouchableOpacity style={styles.button} onPress={() => bringUpActionSheet()} > <Text style={styles.title}>Up</Text> </TouchableOpacity> <View style={styles.fakeContent} /> </View> </SafeAreaView> </Animated.View> </PanGestureHandler> </View> ); } const styles = StyleSheet.create({ container: { position: "absolute", top: 0, left: 0, right: 0, backgroundColor: "white", shadowColor: "black", shadowOffset: { height: -6, width: 0, }, shadowOpacity: 0.1, shadowRadius: 5, borderTopEndRadius: 15, borderTopLeftRadius: 15, }, mapStyle: { width: width, height: 800, }, content: { flex: 1, padding: 20, }, title: { fontWeight: "400", fontSize: 22, }, fakeContent: { flex: 1, height: 1000, }, grbber: { width: 60, alignSelf: "center", marginTop: 5, borderTopWidth: 5, borderTopColor: "#aaa", }, button: { alignItems: "center", justifyContent: "center", paddingVertical: 12, paddingHorizontal: 32, borderRadius: 4, elevation: 3, backgroundColor: "green", marginBottom: 10, height: 50, }, });
修复方案
问题出在两个核心点:
- 错误使用
onGestureEvent回调做状态判断:该回调会在手势滑动过程中连续触发几十次,不适合用来处理手势结束后的逻辑,应该替换为onHandlerStateChange,它只会在手势状态变化时触发。 - 手势逻辑没有区分滑动方向:原代码只判断了手势状态,没有根据上下滑动的偏移量来对应执行展开/收起逻辑,导致不管怎么滑动都会触发收起逻辑。
具体修改步骤:
- 从
react-native-gesture-handler中引入State常量,用于判断手势状态 - 替换手势处理函数,增加滑动偏移量判断
- 修改PanGestureHandler的回调属性
修改后的核心代码如下:
// 第一步:新增引入State import { PanGestureHandler, State, TouchableOpacity, } from "react-native-gesture-handler"; // 第二步:替换手势处理函数 const gestureHandler = (e) => { // 仅在手势结束时判断 if (e.nativeEvent.state === State.END) { // translationY为负代表向上滑动,为正代表向下滑动 const { translationY } = e.nativeEvent; // 向上滑动超过20px阈值,执行展开 if (translationY < -20) { bringUpActionSheet(); } // 向下滑动超过20px阈值,执行收起 else if (translationY > 20) { closeDownBottomSheet(); } } }; // 第三步:修改PanGestureHandler的回调属性 <PanGestureHandler onHandlerStateChange={gestureHandler}>
修改完成后即可实现上滑展开、下滑收起的拖拽效果,同时原有按钮的功能不受影响。
内容的提问来源于stack exchange,提问作者user12494839
相关产品推荐
相关产品推荐

