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
相关产品推荐
相关产品推荐

