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

React Native中Gorhom Bottom Sheet滑动时显示阴影的实现方法

滑动gorhom/react-native-bottom-sheet时动态显示ShadowBox的解决方案

要实现滑动底部弹窗过程中显示ShadowBox,核心是利用组件提供的滑动状态回调,而非依赖Touchable的按压事件(滑动不属于Touchable的触发场景)。以下是两种可行方案:

方案一:通过onChange回调跟踪弹窗滑动进度

BottomSheet组件的onChange方法会在滑动过程中持续触发,返回当前锚点索引index和动画进度值animatedIndex(0到锚点数量的浮点数)。我们可以根据animatedIndex判断是否处于滑动状态,进而控制ShadowBox的显示。

代码示例

import React, { useState, useRef } from 'react';
import BottomSheet from '@gorhom/bottom-sheet';
import { View, StyleSheet } from 'react-native';

const YourComponent = () => {
  const bottomSheetRef = useRef(null);
  const [showShadow, setShowShadow] = useState(false);

  // 监听弹窗状态变化
  const handleSheetChange = (index, animatedIndex) => {
    // animatedIndex > 0 表示弹窗已开始滑动(无论打开/关闭方向)
    setShowShadow(animatedIndex > 0);
  };

  return (
    <View style={styles.container}>
      {/* 阴影盒子,随滑动状态显示/隐藏 */}
      {showShadow && <View style={styles.shadowBox} />}
      
      <BottomSheet
        ref={bottomSheetRef}
        onChange={handleSheetChange}
        snapPoints={['20%', '50%', '90%']}
        // 配置你的弹窗属性(比如backgroundStyle、handleComponent等)
      >
        {/* 弹窗内容区域 */}
        <View style={styles.content}>
          {/* 你的内容 */}
        </View>
      </BottomSheet>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  shadowBox: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
    zIndex: 999,
  },
  content: {
    flex: 1,
    padding: 20,
  },
});

说明

  • animatedIndex是滑动过程中的实时进度值,当它大于0时,说明弹窗已离开关闭位置(处于滑动中或已打开),此时显示ShadowBox;当滑动到完全关闭(animatedIndex === 0)时隐藏。
  • 如果需要平滑的阴影过渡效果,可以用react-native-reanimated将animatedIndex映射为阴影的透明度,替代简单的显示/隐藏。

方案二:通过onGestureEvent监听滑动手势状态

利用BottomSheet的onGestureEvent属性,直接监听滑动手势的活跃状态,更精准地控制ShadowBox的显示时机。

代码示例

import React, { useState, useRef } from 'react';
import BottomSheet from '@gorhom/bottom-sheet';
import { View, StyleSheet } from 'react-native';
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated from 'react-native-reanimated';

const YourComponent = () => {
  const bottomSheetRef = useRef(null);
  const [isGesturing, setIsGesturing] = useState(false);

  const handleGestureEvent = Animated.event(
    [{ nativeEvent: { state: PanGestureHandler.State } }],
    {
      useNativeDriver: false,
      listener: (event) => {
        // 手势活跃时(用户正在滑动)显示阴影
        if (event.nativeEvent.state === PanGestureHandler.State.ACTIVE) {
          setIsGesturing(true);
        } 
        // 手势结束后,根据弹窗当前状态决定是否保留阴影
        else if (event.nativeEvent.state === PanGestureHandler.State.END) {
          const currentIndex = bottomSheetRef.current?.index;
          setIsGesturing(currentIndex > 0);
        }
      },
    }
  );

  return (
    <View style={styles.container}>
      {/* 滑动中或弹窗打开时显示阴影 */}
      {(isGesturing || (bottomSheetRef.current?.index > 0)) && <View style={styles.shadowBox} />}
      
      <BottomSheet
        ref={bottomSheetRef}
        onGestureEvent={handleGestureEvent}
        snapPoints={['20%', '50%', '90%']}
        // 配置你的弹窗属性
      >
        <View style={styles.content}>
          {/* 你的内容 */}
        </View>
      </BottomSheet>
    </View>
  );
};

// 样式同方案一
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  shadowBox: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
    zIndex: 999,
  },
  content: {
    flex: 1,
    padding: 20,
  },
});

说明

  • 当用户开始滑动(手势进入ACTIVE状态),立即显示ShadowBox;手势结束后,若弹窗停留在非关闭锚点(index > 0),则保留阴影,否则隐藏。
  • 这种方式能更精准地响应滑动动作,避免onChange可能存在的微小延迟。

平滑过渡优化(可选)

如果想要阴影随滑动进度渐变显示/隐藏,可以用react-native-reanimated实现:

import React, { useRef } from 'react';
import BottomSheet from '@gorhom/bottom-sheet';
import { View, StyleSheet } from 'react-native';
import Animated, { useSharedValue, interpolate, useAnimatedStyle } from 'react-native-reanimated';

const YourComponent = () => {
  const bottomSheetRef = useRef(null);
  const animatedIndex = useSharedValue(0);

  const handleSheetChange = (index, newAnimatedIndex) => {
    animatedIndex.value = newAnimatedIndex;
  };

  // 将animatedIndex映射为阴影透明度
  const shadowAnimatedStyle = useAnimatedStyle(() => {
    return {
      opacity: interpolate(animatedIndex.value, {
        inputRange: [0, 0.1],
        outputRange: [0, 0.5],
        extrapolateRight: 'clamp',
      }),
    };
  });

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.shadowBox, shadowAnimatedStyle]} />
      
      <BottomSheet
        ref={bottomSheetRef}
        onChange={handleSheetChange}
        snapPoints={['20%', '50%', '90%']}
        // 配置你的弹窗属性
      >
        <View style={styles.content}>
          {/* 你的内容 */}
        </View>
      </BottomSheet>
    </View>
  );
};

内容的提问来源于stack exchange,提问作者Kikoanno

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 03:32:07