xref: /MusicFree/src/components/panels/base/panelFullscreen.tsx (revision 4da0658b1bf77de935d6a60ad1a26e4b92d13606)
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