1756bc302S猫头猫import React, {useCallback, useEffect, useMemo, useRef} from 'react'; 2756bc302S猫头猫import { 3756bc302S猫头猫 BackHandler, 4756bc302S猫头猫 DeviceEventEmitter, 5756bc302S猫头猫 NativeEventSubscription, 6756bc302S猫头猫 Pressable, 7756bc302S猫头猫 StyleSheet, 8756bc302S猫头猫 ViewStyle, 9756bc302S猫头猫} from 'react-native'; 10756bc302S猫头猫 11756bc302S猫头猫import Animated, { 12756bc302S猫头猫 Easing, 13756bc302S猫头猫 EasingFunction, 14756bc302S猫头猫 runOnJS, 15756bc302S猫头猫 useAnimatedReaction, 16756bc302S猫头猫 useAnimatedStyle, 17756bc302S猫头猫 useSharedValue, 18756bc302S猫头猫 withTiming, 19756bc302S猫头猫} from 'react-native-reanimated'; 20756bc302S猫头猫import useColors from '@/hooks/useColors'; 21756bc302S猫头猫import {panelInfoStore} from '../usePanel'; 22756bc302S猫头猫import {vh} from '@/utils/rpx.ts'; 23756bc302S猫头猫import useOrientation from '@/hooks/useOrientation.ts'; 24756bc302S猫头猫 25756bc302S猫头猫const ANIMATION_EASING: EasingFunction = Easing.out(Easing.exp); 26756bc302S猫头猫const ANIMATION_DURATION = 250; 27756bc302S猫头猫 28756bc302S猫头猫const timingConfig = { 29756bc302S猫头猫 duration: ANIMATION_DURATION, 30756bc302S猫头猫 easing: ANIMATION_EASING, 31756bc302S猫头猫}; 32756bc302S猫头猫 33756bc302S猫头猫interface IPanelFullScreenProps { 34756bc302S猫头猫 // 有遮罩 35756bc302S猫头猫 hasMask?: boolean; 36756bc302S猫头猫 // 内容 37756bc302S猫头猫 children?: React.ReactNode; 38756bc302S猫头猫 // 内容区样式 39756bc302S猫头猫 containerStyle?: ViewStyle; 40756bc302S猫头猫 41756bc302S猫头猫 animationType?: 'SlideToTop' | 'Scale'; 42756bc302S猫头猫} 43756bc302S猫头猫 44756bc302S猫头猫export default function (props: IPanelFullScreenProps) { 45756bc302S猫头猫 const { 46756bc302S猫头猫 hasMask, 47756bc302S猫头猫 containerStyle, 48756bc302S猫头猫 children, 49756bc302S猫头猫 animationType = 'SlideToTop', 50756bc302S猫头猫 } = props; 51756bc302S猫头猫 const snapPoint = useSharedValue(0); 52756bc302S猫头猫 53756bc302S猫头猫 const colors = useColors(); 54756bc302S猫头猫 55756bc302S猫头猫 const backHandlerRef = useRef<NativeEventSubscription>(); 56756bc302S猫头猫 57756bc302S猫头猫 const hideCallbackRef = useRef<Function[]>([]); 58756bc302S猫头猫 59756bc302S猫头猫 const orientation = useOrientation(); 60756bc302S猫头猫 const windowHeight = useMemo(() => vh(100), [orientation]); 61756bc302S猫头猫 62756bc302S猫头猫 useEffect(() => { 63*4da0658bSmaotoumao snapPoint.value = 1; 64756bc302S猫头猫 65756bc302S猫头猫 if (backHandlerRef.current) { 66756bc302S猫头猫 backHandlerRef.current?.remove(); 67756bc302S猫头猫 backHandlerRef.current = undefined; 68756bc302S猫头猫 } 69756bc302S猫头猫 backHandlerRef.current = BackHandler.addEventListener( 70756bc302S猫头猫 'hardwareBackPress', 71756bc302S猫头猫 () => { 72*4da0658bSmaotoumao snapPoint.value = 0; 73756bc302S猫头猫 return true; 74756bc302S猫头猫 }, 75756bc302S猫头猫 ); 76756bc302S猫头猫 77756bc302S猫头猫 const listenerSubscription = DeviceEventEmitter.addListener( 78756bc302S猫头猫 'hidePanel', 79756bc302S猫头猫 (callback?: () => void) => { 80756bc302S猫头猫 if (callback) { 81756bc302S猫头猫 hideCallbackRef.current.push(callback); 82756bc302S猫头猫 } 83*4da0658bSmaotoumao snapPoint.value = 0; 84756bc302S猫头猫 }, 85756bc302S猫头猫 ); 86756bc302S猫头猫 87756bc302S猫头猫 return () => { 88756bc302S猫头猫 if (backHandlerRef.current) { 89756bc302S猫头猫 backHandlerRef.current?.remove(); 90756bc302S猫头猫 backHandlerRef.current = undefined; 91756bc302S猫头猫 } 92756bc302S猫头猫 listenerSubscription.remove(); 93756bc302S猫头猫 }; 94756bc302S猫头猫 }, []); 95756bc302S猫头猫 96756bc302S猫头猫 const maskAnimated = useAnimatedStyle(() => { 97756bc302S猫头猫 return { 98756bc302S猫头猫 opacity: withTiming(snapPoint.value * 0.5, timingConfig), 99756bc302S猫头猫 }; 100756bc302S猫头猫 }); 101756bc302S猫头猫 102756bc302S猫头猫 const panelAnimated = useAnimatedStyle(() => { 103756bc302S猫头猫 if (animationType === 'SlideToTop') { 104756bc302S猫头猫 return { 105756bc302S猫头猫 transform: [ 106756bc302S猫头猫 { 107756bc302S猫头猫 translateY: withTiming( 108756bc302S猫头猫 (1 - snapPoint.value) * windowHeight, 109756bc302S猫头猫 timingConfig, 110756bc302S猫头猫 ), 111756bc302S猫头猫 }, 112756bc302S猫头猫 ], 113756bc302S猫头猫 }; 114756bc302S猫头猫 } else { 115756bc302S猫头猫 return { 116756bc302S猫头猫 transform: [ 117756bc302S猫头猫 { 118*4da0658bSmaotoumao scale: withTiming( 119*4da0658bSmaotoumao 0.3 + snapPoint.value * 0.7, 120*4da0658bSmaotoumao timingConfig, 121*4da0658bSmaotoumao ), 122756bc302S猫头猫 }, 123756bc302S猫头猫 ], 124*4da0658bSmaotoumao opacity: withTiming(snapPoint.value, timingConfig), 125756bc302S猫头猫 }; 126756bc302S猫头猫 } 127756bc302S猫头猫 }); 128756bc302S猫头猫 129756bc302S猫头猫 const unmountPanel = useCallback(() => { 130756bc302S猫头猫 panelInfoStore.setValue({ 131756bc302S猫头猫 name: null, 132756bc302S猫头猫 payload: null, 133756bc302S猫头猫 }); 134756bc302S猫头猫 hideCallbackRef.current.forEach(cb => cb?.()); 135756bc302S猫头猫 }, []); 136756bc302S猫头猫 137756bc302S猫头猫 useAnimatedReaction( 138756bc302S猫头猫 () => snapPoint.value, 139756bc302S猫头猫 (result, prevResult) => { 140756bc302S猫头猫 if (prevResult && result < prevResult && result === 0) { 141756bc302S猫头猫 runOnJS(unmountPanel)(); 142756bc302S猫头猫 } 143756bc302S猫头猫 }, 144756bc302S猫头猫 [], 145756bc302S猫头猫 ); 146756bc302S猫头猫 return ( 147756bc302S猫头猫 <> 148756bc302S猫头猫 {hasMask ? ( 149756bc302S猫头猫 <Pressable 150756bc302S猫头猫 style={style.maskWrapper} 151756bc302S猫头猫 onPress={() => { 152756bc302S猫头猫 snapPoint.value = withTiming(0, timingConfig); 153756bc302S猫头猫 }}> 154756bc302S猫头猫 <Animated.View 155756bc302S猫头猫 style={[style.maskWrapper, style.mask, maskAnimated]} 156756bc302S猫头猫 /> 157756bc302S猫头猫 </Pressable> 158756bc302S猫头猫 ) : null} 159756bc302S猫头猫 <Animated.View 160756bc302S猫头猫 pointerEvents={hasMask ? 'box-none' : undefined} 161756bc302S猫头猫 style={[ 162756bc302S猫头猫 style.wrapper, 163756bc302S猫头猫 !hasMask 164756bc302S猫头猫 ? { 165756bc302S猫头猫 backgroundColor: colors.background, 166756bc302S猫头猫 } 167756bc302S猫头猫 : null, 168756bc302S猫头猫 panelAnimated, 169756bc302S猫头猫 containerStyle, 170756bc302S猫头猫 ]}> 171756bc302S猫头猫 {children} 172756bc302S猫头猫 </Animated.View> 173756bc302S猫头猫 </> 174756bc302S猫头猫 ); 175756bc302S猫头猫} 176756bc302S猫头猫 177756bc302S猫头猫const style = StyleSheet.create({ 178756bc302S猫头猫 maskWrapper: { 179756bc302S猫头猫 position: 'absolute', 180756bc302S猫头猫 width: '100%', 181756bc302S猫头猫 height: '100%', 182756bc302S猫头猫 top: 0, 183756bc302S猫头猫 left: 0, 184756bc302S猫头猫 right: 0, 185756bc302S猫头猫 bottom: 0, 186756bc302S猫头猫 zIndex: 15000, 187756bc302S猫头猫 }, 188756bc302S猫头猫 mask: { 189756bc302S猫头猫 backgroundColor: '#000', 190756bc302S猫头猫 opacity: 0.5, 191756bc302S猫头猫 }, 192756bc302S猫头猫 wrapper: { 193756bc302S猫头猫 position: 'absolute', 194756bc302S猫头猫 width: '100%', 195756bc302S猫头猫 height: '100%', 196756bc302S猫头猫 bottom: 0, 197756bc302S猫头猫 right: 0, 198756bc302S猫头猫 zIndex: 15010, 199756bc302S猫头猫 flexDirection: 'column', 200756bc302S猫头猫 }, 201756bc302S猫头猫 kbContainer: { 202756bc302S猫头猫 zIndex: 15010, 203756bc302S猫头猫 }, 204756bc302S猫头猫}); 205