You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,
  },
});
修复方案

问题出在两个核心点:

  1. 错误使用onGestureEvent回调做状态判断:该回调会在手势滑动过程中连续触发几十次,不适合用来处理手势结束后的逻辑,应该替换为onHandlerStateChange,它只会在手势状态变化时触发。
  2. 手势逻辑没有区分滑动方向:原代码只判断了手势状态,没有根据上下滑动的偏移量来对应执行展开/收起逻辑,导致不管怎么滑动都会触发收起逻辑。

具体修改步骤:

  1. 从react-native-gesture-handler中引入State常量,用于判断手势状态
  2. 替换手势处理函数,增加滑动偏移量判断
  3. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 11:54:03