xref: /MusicFree/src/components/musicBar/musicInfo.tsx (revision 62e73a5ee3cc093901d1c9f69ebb5af095cea2b2)
1*62e73a5eS猫头猫import React, {memo, useLayoutEffect, useMemo} from 'react';
2f511aee9S猫头猫import {StyleSheet, Text, View} from 'react-native';
3f511aee9S猫头猫import rpx from '@/utils/rpx';
4f511aee9S猫头猫import FastImage from '../base/fastImage';
5f511aee9S猫头猫import {ImgAsset} from '@/constants/assetsConst';
6f511aee9S猫头猫import Color from 'color';
7f511aee9S猫头猫import ThemeText from '../base/themeText';
8f511aee9S猫头猫import useColors from '@/hooks/useColors';
9f511aee9S猫头猫import {ROUTE_PATH, useNavigate} from '@/entry/router';
10f511aee9S猫头猫import {Gesture, GestureDetector} from 'react-native-gesture-handler';
11f511aee9S猫头猫import TrackPlayer from '@/core/trackPlayer';
12f511aee9S猫头猫import Animated, {
13f511aee9S猫头猫    SharedValue,
14f511aee9S猫头猫    runOnJS,
15f511aee9S猫头猫    useAnimatedStyle,
16f511aee9S猫头猫    useSharedValue,
17f511aee9S猫头猫    withTiming,
18f511aee9S猫头猫} from 'react-native-reanimated';
19f511aee9S猫头猫import {useSafeAreaInsets} from 'react-native-safe-area-context';
20f511aee9S猫头猫import {timingConfig} from '@/constants/commonConst';
21f511aee9S猫头猫
22f511aee9S猫头猫interface IBarMusicItemProps {
23f511aee9S猫头猫    musicItem: IMusic.IMusicItem | null;
24f511aee9S猫头猫    activeIndex: number; // 当前展示的是0/1/2
25f511aee9S猫头猫    transformSharedValue: SharedValue<number>;
26f511aee9S猫头猫}
27f511aee9S猫头猫function _BarMusicItem(props: IBarMusicItemProps) {
28f511aee9S猫头猫    const {musicItem, activeIndex, transformSharedValue} = props;
29f511aee9S猫头猫    const colors = useColors();
30f511aee9S猫头猫    const safeAreaInsets = useSafeAreaInsets();
31f511aee9S猫头猫
32f511aee9S猫头猫    const animatedStyles = useAnimatedStyle(() => {
33f511aee9S猫头猫        return {
34f511aee9S猫头猫            left: `${(transformSharedValue.value + activeIndex) * 100}%`,
35f511aee9S猫头猫        };
36f511aee9S猫头猫    }, [activeIndex]);
37f511aee9S猫头猫
38f511aee9S猫头猫    if (!musicItem) {
39f511aee9S猫头猫        return null;
40f511aee9S猫头猫    }
41f511aee9S猫头猫
42f511aee9S猫头猫    return (
43f511aee9S猫头猫        <Animated.View
44f511aee9S猫头猫            style={[
45f511aee9S猫头猫                styles.container,
46f511aee9S猫头猫                {
47f511aee9S猫头猫                    paddingLeft: rpx(24) + safeAreaInsets.left,
48f511aee9S猫头猫                },
49f511aee9S猫头猫                animatedStyles,
50f511aee9S猫头猫            ]}>
51f511aee9S猫头猫            <FastImage
52f511aee9S猫头猫                style={styles.artworkImg}
53f511aee9S猫头猫                uri={musicItem.artwork}
54f511aee9S猫头猫                emptySrc={ImgAsset.albumDefault}
55f511aee9S猫头猫            />
56f511aee9S猫头猫            <Text
57f511aee9S猫头猫                ellipsizeMode="tail"
58f511aee9S猫头猫                accessible={false}
59f511aee9S猫头猫                style={styles.textWrapper}
60f511aee9S猫头猫                numberOfLines={1}>
61f511aee9S猫头猫                <ThemeText fontSize="content" fontColor="musicBarText">
62f511aee9S猫头猫                    {musicItem?.title}
63f511aee9S猫头猫                </ThemeText>
64f511aee9S猫头猫                {musicItem?.artist && (
65f511aee9S猫头猫                    <ThemeText
66f511aee9S猫头猫                        fontSize="description"
67f511aee9S猫头猫                        color={Color(colors.musicBarText)
68f511aee9S猫头猫                            .alpha(0.6)
69f511aee9S猫头猫                            .toString()}>
70f511aee9S猫头猫                        {' '}
71f511aee9S猫头猫                        -{musicItem.artist}
72f511aee9S猫头猫                    </ThemeText>
73f511aee9S猫头猫                )}
74f511aee9S猫头猫            </Text>
75f511aee9S猫头猫        </Animated.View>
76f511aee9S猫头猫    );
77f511aee9S猫头猫}
78f511aee9S猫头猫
79f511aee9S猫头猫const BarMusicItem = memo(
80f511aee9S猫头猫    _BarMusicItem,
81f511aee9S猫头猫    (prev, curr) =>
82f511aee9S猫头猫        prev.musicItem === curr.musicItem &&
83f511aee9S猫头猫        prev.activeIndex === curr.activeIndex,
84f511aee9S猫头猫);
85f511aee9S猫头猫
86f511aee9S猫头猫const styles = StyleSheet.create({
87f511aee9S猫头猫    container: {
88f511aee9S猫头猫        flexDirection: 'row',
89f511aee9S猫头猫        width: '100%',
90f511aee9S猫头猫        alignItems: 'center',
91f511aee9S猫头猫        position: 'absolute',
92f511aee9S猫头猫    },
93f511aee9S猫头猫    textWrapper: {
94f511aee9S猫头猫        flexGrow: 1,
95f511aee9S猫头猫        flexShrink: 1,
96f511aee9S猫头猫    },
97f511aee9S猫头猫    artworkImg: {
98f511aee9S猫头猫        width: rpx(96),
99f511aee9S猫头猫        height: rpx(96),
100f511aee9S猫头猫        borderRadius: rpx(48),
101f511aee9S猫头猫        marginRight: rpx(24),
102f511aee9S猫头猫    },
103f511aee9S猫头猫});
104f511aee9S猫头猫
105f511aee9S猫头猫interface IMusicInfoProps {
106f511aee9S猫头猫    musicItem: IMusic.IMusicItem | null;
107f511aee9S猫头猫    paddingLeft?: number;
108f511aee9S猫头猫}
109f511aee9S猫头猫
110f511aee9S猫头猫function skipMusicItem(direction: number) {
111f511aee9S猫头猫    if (direction === -1) {
112f511aee9S猫头猫        TrackPlayer.skipToNext();
113f511aee9S猫头猫    } else if (direction === 1) {
114f511aee9S猫头猫        TrackPlayer.skipToPrevious();
115f511aee9S猫头猫    }
116f511aee9S猫头猫}
117f511aee9S猫头猫
118f511aee9S猫头猫export default function MusicInfo(props: IMusicInfoProps) {
119f511aee9S猫头猫    const {musicItem} = props;
120f511aee9S猫头猫    const navigate = useNavigate();
121c78b5264S猫头猫    const playLists = TrackPlayer.usePlayList();
122f511aee9S猫头猫    const siblingMusicItems = useMemo(() => {
123f511aee9S猫头猫        if (!musicItem) {
124f511aee9S猫头猫            return {
125f511aee9S猫头猫                prev: null,
126f511aee9S猫头猫                next: null,
127f511aee9S猫头猫            };
128f511aee9S猫头猫        }
129f511aee9S猫头猫        return {
130f511aee9S猫头猫            prev: TrackPlayer.getPreviousMusic(),
131f511aee9S猫头猫            next: TrackPlayer.getNextMusic(),
132f511aee9S猫头猫        };
133c78b5264S猫头猫    }, [musicItem, playLists]);
134f511aee9S猫头猫
135f511aee9S猫头猫    // +- 1
136f511aee9S猫头猫    const transformSharedValue = useSharedValue(0);
137f511aee9S猫头猫
138*62e73a5eS猫头猫    const musicItemWidthValue = useSharedValue(0);
139f511aee9S猫头猫
140f511aee9S猫头猫    const tapGesture = Gesture.Tap()
141f511aee9S猫头猫        .onStart(() => {
142f511aee9S猫头猫            navigate(ROUTE_PATH.MUSIC_DETAIL);
143f511aee9S猫头猫        })
144f511aee9S猫头猫        .runOnJS(true);
145f511aee9S猫头猫
146f511aee9S猫头猫    useLayoutEffect(() => {
147f511aee9S猫头猫        transformSharedValue.value = 0;
148f511aee9S猫头猫    }, [musicItem]);
149f511aee9S猫头猫
150f511aee9S猫头猫    const panGesture = Gesture.Pan()
151f511aee9S猫头猫        .minPointers(1)
152f511aee9S猫头猫        .maxPointers(1)
153f511aee9S猫头猫        .onUpdate(e => {
154*62e73a5eS猫头猫            if (musicItemWidthValue.value) {
155f511aee9S猫头猫                transformSharedValue.value =
156*62e73a5eS猫头猫                    e.translationX / musicItemWidthValue.value;
157f511aee9S猫头猫            }
158f511aee9S猫头猫        })
159f511aee9S猫头猫        .onEnd((e, success) => {
160f511aee9S猫头猫            if (!success) {
161f511aee9S猫头猫                // 还原到原始位置
162f511aee9S猫头猫                transformSharedValue.value = withTiming(
163f511aee9S猫头猫                    0,
164f511aee9S猫头猫                    timingConfig.animationFast,
165f511aee9S猫头猫                );
166f511aee9S猫头猫            } else {
167f511aee9S猫头猫                // fling
168f511aee9S猫头猫                const deltaX = e.translationX;
169f511aee9S猫头猫                const vX = e.velocityX;
170f511aee9S猫头猫
171f511aee9S猫头猫                let skip = 0;
172*62e73a5eS猫头猫                if (musicItemWidthValue.value) {
173*62e73a5eS猫头猫                    const rate = deltaX / musicItemWidthValue.value;
174f511aee9S猫头猫
175f511aee9S猫头猫                    if (Math.abs(rate) > 0.3) {
176f511aee9S猫头猫                        // 先判断距离
177f511aee9S猫头猫                        skip = vX > 0 ? 1 : -1;
178f511aee9S猫头猫                        transformSharedValue.value = withTiming(
179f511aee9S猫头猫                            skip,
180f511aee9S猫头猫                            timingConfig.animationFast,
181f511aee9S猫头猫                            () => {
182f511aee9S猫头猫                                runOnJS(skipMusicItem)(skip);
183f511aee9S猫头猫                            },
184f511aee9S猫头猫                        );
185f511aee9S猫头猫                    } else if (Math.abs(vX) > 1500) {
186f511aee9S猫头猫                        // 再判断速度
187f511aee9S猫头猫                        skip = vX > 0 ? 1 : -1;
188f511aee9S猫头猫                        transformSharedValue.value = skip;
189f511aee9S猫头猫                        runOnJS(skipMusicItem)(skip);
190f511aee9S猫头猫                    } else {
191f511aee9S猫头猫                        transformSharedValue.value = withTiming(
192f511aee9S猫头猫                            0,
193f511aee9S猫头猫                            timingConfig.animationFast,
194f511aee9S猫头猫                        );
195f511aee9S猫头猫                    }
196f511aee9S猫头猫                } else {
197f511aee9S猫头猫                    transformSharedValue.value = 0;
198f511aee9S猫头猫                }
199f511aee9S猫头猫            }
200f511aee9S猫头猫        });
201f511aee9S猫头猫
202f511aee9S猫头猫    const gesture = Gesture.Race(panGesture, tapGesture);
203f511aee9S猫头猫
204f511aee9S猫头猫    return (
205f511aee9S猫头猫        <GestureDetector gesture={gesture}>
206f511aee9S猫头猫            <View
207f511aee9S猫头猫                style={musicInfoStyles.infoContainer}
208f511aee9S猫头猫                onLayout={e => {
209*62e73a5eS猫头猫                    musicItemWidthValue.value = e.nativeEvent.layout.width;
210f511aee9S猫头猫                }}>
211f511aee9S猫头猫                <BarMusicItem
212f511aee9S猫头猫                    transformSharedValue={transformSharedValue}
213f511aee9S猫头猫                    musicItem={siblingMusicItems.prev}
214f511aee9S猫头猫                    activeIndex={-1}
215f511aee9S猫头猫                />
216f511aee9S猫头猫                <BarMusicItem
217f511aee9S猫头猫                    transformSharedValue={transformSharedValue}
218f511aee9S猫头猫                    musicItem={musicItem}
219f511aee9S猫头猫                    activeIndex={0}
220f511aee9S猫头猫                />
221f511aee9S猫头猫                <BarMusicItem
222f511aee9S猫头猫                    transformSharedValue={transformSharedValue}
223f511aee9S猫头猫                    musicItem={siblingMusicItems.next}
224f511aee9S猫头猫                    activeIndex={1}
225f511aee9S猫头猫                />
226f511aee9S猫头猫            </View>
227f511aee9S猫头猫        </GestureDetector>
228f511aee9S猫头猫    );
229f511aee9S猫头猫}
230f511aee9S猫头猫
231f511aee9S猫头猫const musicInfoStyles = StyleSheet.create({
232f511aee9S猫头猫    infoContainer: {
233f511aee9S猫头猫        flex: 1,
234f511aee9S猫头猫        height: '100%',
235f511aee9S猫头猫        alignItems: 'center',
236f511aee9S猫头猫        flexDirection: 'row',
237f511aee9S猫头猫        overflow: 'hidden',
238f511aee9S猫头猫    },
239f511aee9S猫头猫});
240