1ec4205c4S猫头猫import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; 2ec4205c4S猫头猫import { 3ec4205c4S猫头猫 BackHandler, 4ec4205c4S猫头猫 DeviceEventEmitter, 5*209bed7bS猫头猫 EmitterSubscription, 6*209bed7bS猫头猫 Keyboard, 7cf3527d9S猫头猫 KeyboardAvoidingView, 8ec4205c4S猫头猫 NativeEventSubscription, 9ec4205c4S猫头猫 Pressable, 10ec4205c4S猫头猫 StyleSheet, 11ec4205c4S猫头猫} from 'react-native'; 12ec4205c4S猫头猫import rpx, {vh} from '@/utils/rpx'; 13ec4205c4S猫头猫 14ec4205c4S猫头猫import Animated, { 15ec4205c4S猫头猫 Easing, 16ec4205c4S猫头猫 runOnJS, 17ec4205c4S猫头猫 useAnimatedReaction, 18ec4205c4S猫头猫 useAnimatedStyle, 19ec4205c4S猫头猫 useSharedValue, 20ec4205c4S猫头猫 withTiming, 21ec4205c4S猫头猫} from 'react-native-reanimated'; 22ec4205c4S猫头猫import useColors from '@/hooks/useColors'; 23ec4205c4S猫头猫import {useSafeAreaInsets} from 'react-native-safe-area-context'; 24ec4205c4S猫头猫import useOrientation from '@/hooks/useOrientation'; 25ec4205c4S猫头猫import {panelInfoStore} from '../usePanel'; 26ec4205c4S猫头猫 27ec4205c4S猫头猫const ANIMATION_EASING: Animated.EasingFunction = Easing.out(Easing.exp); 28ec4205c4S猫头猫const ANIMATION_DURATION = 250; 29ec4205c4S猫头猫 30ec4205c4S猫头猫const timingConfig = { 31ec4205c4S猫头猫 duration: ANIMATION_DURATION, 32ec4205c4S猫头猫 easing: ANIMATION_EASING, 33ec4205c4S猫头猫}; 34ec4205c4S猫头猫 35ec4205c4S猫头猫interface IPanelBaseProps { 36428a0723S猫头猫 keyboardAvoidBehavior?: 'height' | 'padding' | 'position' | 'none'; 37ec4205c4S猫头猫 height?: number; 38ec4205c4S猫头猫 renderBody: (loading: boolean) => JSX.Element; 39*209bed7bS猫头猫 awareKeyboard?: boolean; 40ec4205c4S猫头猫} 41ec4205c4S猫头猫 42ec4205c4S猫头猫export default function (props: IPanelBaseProps) { 43*209bed7bS猫头猫 const { 44*209bed7bS猫头猫 height = vh(60), 45*209bed7bS猫头猫 renderBody, 46*209bed7bS猫头猫 keyboardAvoidBehavior, 47*209bed7bS猫头猫 awareKeyboard, 48*209bed7bS猫头猫 } = props; 49ec4205c4S猫头猫 const snapPoint = useSharedValue(0); 50ec4205c4S猫头猫 51ec4205c4S猫头猫 const colors = useColors(); 52ec4205c4S猫头猫 const [loading, setLoading] = useState(true); // 是否处于弹出状态 53ec4205c4S猫头猫 const timerRef = useRef<any>(); 54ec4205c4S猫头猫 const safeAreaInsets = useSafeAreaInsets(); 55ec4205c4S猫头猫 const orientation = useOrientation(); 56ec4205c4S猫头猫 const useAnimatedBase = useMemo( 57ec4205c4S猫头猫 () => (orientation === 'horizonal' ? rpx(750) : height), 58ec4205c4S猫头猫 [orientation], 59ec4205c4S猫头猫 ); 60ec4205c4S猫头猫 const backHandlerRef = useRef<NativeEventSubscription>(); 61ec4205c4S猫头猫 62ec4205c4S猫头猫 const hideCallbackRef = useRef<Function[]>([]); 63ec4205c4S猫头猫 64*209bed7bS猫头猫 const [keyboardHeight, setKeyboardHeight] = useState(0); 65ec4205c4S猫头猫 useEffect(() => { 66ec4205c4S猫头猫 snapPoint.value = withTiming(1, timingConfig); 67ec4205c4S猫头猫 timerRef.current = setTimeout(() => { 68ec4205c4S猫头猫 if (loading) { 69ec4205c4S猫头猫 // 兜底 70ec4205c4S猫头猫 setLoading(false); 71ec4205c4S猫头猫 } 72ec4205c4S猫头猫 }, 400); 73ec4205c4S猫头猫 if (backHandlerRef.current) { 74ec4205c4S猫头猫 backHandlerRef.current?.remove(); 75ec4205c4S猫头猫 backHandlerRef.current = undefined; 76ec4205c4S猫头猫 } 77ec4205c4S猫头猫 backHandlerRef.current = BackHandler.addEventListener( 78ec4205c4S猫头猫 'hardwareBackPress', 79ec4205c4S猫头猫 () => { 80ec4205c4S猫头猫 snapPoint.value = withTiming(0, timingConfig); 81ec4205c4S猫头猫 return true; 82ec4205c4S猫头猫 }, 83ec4205c4S猫头猫 ); 84ec4205c4S猫头猫 85ec4205c4S猫头猫 const listenerSubscription = DeviceEventEmitter.addListener( 86ec4205c4S猫头猫 'hidePanel', 87ec4205c4S猫头猫 (callback?: () => void) => { 88ec4205c4S猫头猫 if (callback) { 89ec4205c4S猫头猫 hideCallbackRef.current.push(callback); 90ec4205c4S猫头猫 } 91ec4205c4S猫头猫 snapPoint.value = withTiming(0, timingConfig); 92ec4205c4S猫头猫 }, 93ec4205c4S猫头猫 ); 94ec4205c4S猫头猫 95*209bed7bS猫头猫 let keyboardDidShowListener: EmitterSubscription; 96*209bed7bS猫头猫 let keyboardDidHideListener: EmitterSubscription; 97*209bed7bS猫头猫 if (awareKeyboard) { 98*209bed7bS猫头猫 Keyboard.addListener('keyboardDidChangeFrame', event => { 99*209bed7bS猫头猫 console.log(event, 'KKss'); 100*209bed7bS猫头猫 }); 101*209bed7bS猫头猫 Keyboard.addListener('keyboardWillShow', event => { 102*209bed7bS猫头猫 console.log(event, 'KKsss'); 103*209bed7bS猫头猫 }); 104*209bed7bS猫头猫 keyboardDidShowListener = Keyboard.addListener( 105*209bed7bS猫头猫 'keyboardDidShow', 106*209bed7bS猫头猫 event => { 107*209bed7bS猫头猫 setKeyboardHeight(event.endCoordinates.height); 108*209bed7bS猫头猫 }, 109*209bed7bS猫头猫 ); 110*209bed7bS猫头猫 111*209bed7bS猫头猫 keyboardDidHideListener = Keyboard.addListener( 112*209bed7bS猫头猫 'keyboardDidHide', 113*209bed7bS猫头猫 () => { 114*209bed7bS猫头猫 setKeyboardHeight(0); 115*209bed7bS猫头猫 }, 116*209bed7bS猫头猫 ); 117*209bed7bS猫头猫 } 118*209bed7bS猫头猫 119ec4205c4S猫头猫 return () => { 120ec4205c4S猫头猫 if (timerRef.current) { 121ec4205c4S猫头猫 clearTimeout(timerRef.current); 122ec4205c4S猫头猫 timerRef.current = null; 123ec4205c4S猫头猫 } 124ec4205c4S猫头猫 if (backHandlerRef.current) { 125ec4205c4S猫头猫 backHandlerRef.current?.remove(); 126ec4205c4S猫头猫 backHandlerRef.current = undefined; 127ec4205c4S猫头猫 } 128ec4205c4S猫头猫 listenerSubscription.remove(); 129*209bed7bS猫头猫 keyboardDidShowListener?.remove(); 130*209bed7bS猫头猫 keyboardDidHideListener?.remove(); 131ec4205c4S猫头猫 }; 132ec4205c4S猫头猫 }, []); 133ec4205c4S猫头猫 134ec4205c4S猫头猫 const maskAnimated = useAnimatedStyle(() => { 135ec4205c4S猫头猫 return { 136756bc302S猫头猫 opacity: snapPoint.value * 0.5, 137ec4205c4S猫头猫 }; 138ec4205c4S猫头猫 }); 139ec4205c4S猫头猫 140ec4205c4S猫头猫 const panelAnimated = useAnimatedStyle(() => { 141ec4205c4S猫头猫 return { 142ec4205c4S猫头猫 transform: [ 143ec4205c4S猫头猫 orientation === 'vertical' 144ec4205c4S猫头猫 ? { 145756bc302S猫头猫 translateY: (1 - snapPoint.value) * useAnimatedBase, 146ec4205c4S猫头猫 } 147ec4205c4S猫头猫 : { 148756bc302S猫头猫 translateX: (1 - snapPoint.value) * useAnimatedBase, 149ec4205c4S猫头猫 }, 150ec4205c4S猫头猫 ], 151ec4205c4S猫头猫 }; 152ec4205c4S猫头猫 }, [orientation]); 153ec4205c4S猫头猫 154ec4205c4S猫头猫 const mountPanel = useCallback(() => { 155ec4205c4S猫头猫 setLoading(false); 156ec4205c4S猫头猫 }, []); 157ec4205c4S猫头猫 158ec4205c4S猫头猫 const unmountPanel = useCallback(() => { 159ec4205c4S猫头猫 panelInfoStore.setValue({ 160ec4205c4S猫头猫 name: null, 161ec4205c4S猫头猫 payload: null, 162ec4205c4S猫头猫 }); 163ec4205c4S猫头猫 hideCallbackRef.current.forEach(cb => cb?.()); 164ec4205c4S猫头猫 }, []); 165ec4205c4S猫头猫 166ec4205c4S猫头猫 useAnimatedReaction( 167ec4205c4S猫头猫 () => snapPoint.value, 168ec4205c4S猫头猫 (result, prevResult) => { 169ec4205c4S猫头猫 if (prevResult && result > prevResult && result > 0.8) { 170ec4205c4S猫头猫 runOnJS(mountPanel)(); 171ec4205c4S猫头猫 } 172ec4205c4S猫头猫 if (prevResult && result < prevResult && result === 0) { 173ec4205c4S猫头猫 runOnJS(unmountPanel)(); 174ec4205c4S猫头猫 } 175ec4205c4S猫头猫 }, 176ec4205c4S猫头猫 [], 177ec4205c4S猫头猫 ); 178ec4205c4S猫头猫 179428a0723S猫头猫 const panelBody = ( 180ec4205c4S猫头猫 <Animated.View 181ec4205c4S猫头猫 style={[ 182ec4205c4S猫头猫 style.wrapper, 183ec4205c4S猫头猫 { 184277c5280S猫头猫 backgroundColor: colors.backdrop, 185ec4205c4S猫头猫 height: 186ec4205c4S猫头猫 orientation === 'horizonal' 187ec4205c4S猫头猫 ? vh(100) - safeAreaInsets.top 188*209bed7bS猫头猫 : height - keyboardHeight, 189ec4205c4S猫头猫 }, 190ec4205c4S猫头猫 panelAnimated, 191ec4205c4S猫头猫 ]}> 192ec4205c4S猫头猫 {renderBody(loading)} 193ec4205c4S猫头猫 </Animated.View> 194428a0723S猫头猫 ); 195428a0723S猫头猫 196428a0723S猫头猫 return ( 197428a0723S猫头猫 <> 198428a0723S猫头猫 <Pressable 199428a0723S猫头猫 style={style.maskWrapper} 200428a0723S猫头猫 onPress={() => { 201428a0723S猫头猫 snapPoint.value = withTiming(0, timingConfig); 202428a0723S猫头猫 }}> 203428a0723S猫头猫 <Animated.View 204428a0723S猫头猫 style={[style.maskWrapper, style.mask, maskAnimated]} 205428a0723S猫头猫 /> 206428a0723S猫头猫 </Pressable> 207428a0723S猫头猫 {keyboardAvoidBehavior === 'none' ? ( 208428a0723S猫头猫 panelBody 209428a0723S猫头猫 ) : ( 210428a0723S猫头猫 <KeyboardAvoidingView 211fe32deaaS猫头猫 style={style.kbContainer} 212428a0723S猫头猫 behavior={keyboardAvoidBehavior || 'position'}> 213428a0723S猫头猫 {panelBody} 214cf3527d9S猫头猫 </KeyboardAvoidingView> 215428a0723S猫头猫 )} 216ec4205c4S猫头猫 </> 217ec4205c4S猫头猫 ); 218ec4205c4S猫头猫} 219ec4205c4S猫头猫 220ec4205c4S猫头猫const style = StyleSheet.create({ 221ec4205c4S猫头猫 maskWrapper: { 222ec4205c4S猫头猫 position: 'absolute', 223ec4205c4S猫头猫 width: '100%', 224ec4205c4S猫头猫 height: '100%', 225ec4205c4S猫头猫 top: 0, 226ec4205c4S猫头猫 left: 0, 227ec4205c4S猫头猫 right: 0, 228ec4205c4S猫头猫 bottom: 0, 229fe32deaaS猫头猫 zIndex: 15000, 230ec4205c4S猫头猫 }, 231ec4205c4S猫头猫 mask: { 232277c5280S猫头猫 backgroundColor: '#000', 233ec4205c4S猫头猫 opacity: 0.5, 234ec4205c4S猫头猫 }, 235ec4205c4S猫头猫 wrapper: { 236ec4205c4S猫头猫 position: 'absolute', 237ec4205c4S猫头猫 width: rpx(750), 238ec4205c4S猫头猫 bottom: 0, 239ec4205c4S猫头猫 right: 0, 240ec4205c4S猫头猫 borderTopLeftRadius: rpx(28), 241ec4205c4S猫头猫 borderTopRightRadius: rpx(28), 242fe32deaaS猫头猫 zIndex: 15010, 243fe32deaaS猫头猫 }, 244fe32deaaS猫头猫 kbContainer: { 245fe32deaaS猫头猫 zIndex: 15010, 246ec4205c4S猫头猫 }, 247ec4205c4S猫头猫}); 248