18d82ecd9S猫头猫import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; 28d82ecd9S猫头猫import {LayoutRectangle, StyleSheet, Text, View} from 'react-native'; 38d82ecd9S猫头猫import rpx from '@/utils/rpx'; 48d82ecd9S猫头猫import useDelayFalsy from '@/hooks/useDelayFalsy'; 58d82ecd9S猫头猫import {FlatList, TapGestureHandler} from 'react-native-gesture-handler'; 68d82ecd9S猫头猫import {fontSizeConst} from '@/constants/uiConst'; 78d82ecd9S猫头猫import {IconButtonWithGesture} from '@/components/base/iconButton'; 88d82ecd9S猫头猫import Loading from '@/components/base/loading'; 98d82ecd9S猫头猫import globalStyle from '@/constants/globalStyle'; 108d82ecd9S猫头猫import {showPanel} from '@/components/panels/usePanel'; 118d82ecd9S猫头猫import LyricManager from '@/core/lyricManager'; 128d82ecd9S猫头猫import TrackPlayer from '@/core/trackPlayer'; 138d82ecd9S猫头猫import {musicIsPaused} from '@/utils/trackUtils'; 148d82ecd9S猫头猫import delay from '@/utils/delay'; 158d82ecd9S猫头猫import DraggingTime from './draggingTime'; 168d82ecd9S猫头猫import LyricItemComponent from './lyricItem'; 178d82ecd9S猫头猫 188d82ecd9S猫头猫const ITEM_HEIGHT = rpx(92); 198d82ecd9S猫头猫 208d82ecd9S猫头猫interface IItemHeights { 218d82ecd9S猫头猫 blankHeight?: number; 228d82ecd9S猫头猫 [k: number]: number; 238d82ecd9S猫头猫} 248d82ecd9S猫头猫 258d82ecd9S猫头猫export default function Lyric() { 268d82ecd9S猫头猫 const {loading, meta, lyrics: lyric} = LyricManager.useLyricState(); 278d82ecd9S猫头猫 const currentLrcItem = LyricManager.useCurrentLyric(); 288d82ecd9S猫头猫 298d82ecd9S猫头猫 // 是否展示拖拽 308d82ecd9S猫头猫 const dragShownRef = useRef(false); 318d82ecd9S猫头猫 const [draggingIndex, setDraggingIndex, setDraggingIndexImmi] = 328d82ecd9S猫头猫 useDelayFalsy<number | undefined>(undefined, 2000); 338d82ecd9S猫头猫 const listRef = useRef<FlatList<ILyric.IParsedLrcItem> | null>(); 348d82ecd9S猫头猫 const musicState = TrackPlayer.useMusicState(); 358d82ecd9S猫头猫 368d82ecd9S猫头猫 const [layout, setLayout] = useState<LayoutRectangle>(); 378d82ecd9S猫头猫 388d82ecd9S猫头猫 // 用来缓存高度 398d82ecd9S猫头猫 const itemHeightsRef = useRef<IItemHeights>({}); 408d82ecd9S猫头猫 418d82ecd9S猫头猫 // 设置空白组件,获取组件高度 428d82ecd9S猫头猫 const blankComponent = useMemo(() => { 438d82ecd9S猫头猫 return ( 448d82ecd9S猫头猫 <View 458d82ecd9S猫头猫 style={style.empty} 468d82ecd9S猫头猫 onLayout={evt => { 478d82ecd9S猫头猫 itemHeightsRef.current.blankHeight = 488d82ecd9S猫头猫 evt.nativeEvent.layout.height; 498d82ecd9S猫头猫 }} 508d82ecd9S猫头猫 /> 518d82ecd9S猫头猫 ); 528d82ecd9S猫头猫 }, []); 538d82ecd9S猫头猫 548d82ecd9S猫头猫 const handleLyricItemLayout = useCallback( 558d82ecd9S猫头猫 (index: number, height: number) => { 568d82ecd9S猫头猫 itemHeightsRef.current[index] = height; 578d82ecd9S猫头猫 }, 588d82ecd9S猫头猫 [], 598d82ecd9S猫头猫 ); 608d82ecd9S猫头猫 618d82ecd9S猫头猫 useEffect(() => { 628d82ecd9S猫头猫 // 暂停且拖拽才返回 638d82ecd9S猫头猫 if ( 648d82ecd9S猫头猫 lyric.length === 0 || 658d82ecd9S猫头猫 draggingIndex !== undefined || 668d82ecd9S猫头猫 (draggingIndex === undefined && musicIsPaused(musicState)) || 678d82ecd9S猫头猫 lyric[lyric.length - 1].time < 1 688d82ecd9S猫头猫 ) { 698d82ecd9S猫头猫 return; 708d82ecd9S猫头猫 } 718d82ecd9S猫头猫 if (currentLrcItem?.index === -1 || !currentLrcItem) { 728d82ecd9S猫头猫 listRef.current?.scrollToIndex({ 738d82ecd9S猫头猫 index: 0, 748d82ecd9S猫头猫 viewPosition: 0.5, 758d82ecd9S猫头猫 }); 768d82ecd9S猫头猫 } else { 778d82ecd9S猫头猫 listRef.current?.scrollToIndex({ 788d82ecd9S猫头猫 index: Math.min(currentLrcItem.index ?? 0, lyric.length - 1), 798d82ecd9S猫头猫 viewPosition: 0.5, 808d82ecd9S猫头猫 }); 818d82ecd9S猫头猫 } 828d82ecd9S猫头猫 // 音乐暂停状态不应该影响到滑动,所以不放在依赖里,但是这样写不好。。 838d82ecd9S猫头猫 }, [currentLrcItem, lyric, draggingIndex]); 848d82ecd9S猫头猫 85*a48a4bb0S猫头猫 useEffect(() => { 86*a48a4bb0S猫头猫 if (currentLrcItem?.index !== undefined && currentLrcItem.index > -1) { 87*a48a4bb0S猫头猫 listRef.current?.scrollToIndex({ 88*a48a4bb0S猫头猫 index: currentLrcItem.index, 89*a48a4bb0S猫头猫 viewPosition: 0.5, 90*a48a4bb0S猫头猫 animated: false, 91*a48a4bb0S猫头猫 }); 92*a48a4bb0S猫头猫 } 93*a48a4bb0S猫头猫 }, []); 94*a48a4bb0S猫头猫 958d82ecd9S猫头猫 // 开始滚动时拖拽生效 968d82ecd9S猫头猫 const onScrollBeginDrag = () => { 978d82ecd9S猫头猫 dragShownRef.current = true; 988d82ecd9S猫头猫 }; 998d82ecd9S猫头猫 1008d82ecd9S猫头猫 const onScrollEndDrag = async () => { 1018d82ecd9S猫头猫 if (draggingIndex !== undefined) { 1028d82ecd9S猫头猫 setDraggingIndex(undefined); 1038d82ecd9S猫头猫 } 1048d82ecd9S猫头猫 dragShownRef.current = false; 1058d82ecd9S猫头猫 }; 1068d82ecd9S猫头猫 1078d82ecd9S猫头猫 const onScroll = (e: any) => { 1088d82ecd9S猫头猫 if (dragShownRef.current) { 1098d82ecd9S猫头猫 const offset = 1108d82ecd9S猫头猫 e.nativeEvent.contentOffset.y + 1118d82ecd9S猫头猫 e.nativeEvent.layoutMeasurement.height / 2; 1128d82ecd9S猫头猫 1138d82ecd9S猫头猫 const itemHeights = itemHeightsRef.current; 1148d82ecd9S猫头猫 let height = itemHeights.blankHeight!; 1158d82ecd9S猫头猫 if (offset <= height) { 1168d82ecd9S猫头猫 setDraggingIndex(0); 1178d82ecd9S猫头猫 return; 1188d82ecd9S猫头猫 } 1198d82ecd9S猫头猫 for (let i = 0; i < lyric.length; ++i) { 1208d82ecd9S猫头猫 height += itemHeights[i] ?? 0; 1218d82ecd9S猫头猫 if (height > offset) { 1228d82ecd9S猫头猫 setDraggingIndex(i); 1238d82ecd9S猫头猫 return; 1248d82ecd9S猫头猫 } 1258d82ecd9S猫头猫 } 1268d82ecd9S猫头猫 } 1278d82ecd9S猫头猫 }; 1288d82ecd9S猫头猫 1298d82ecd9S猫头猫 const onLyricSeekPress = async () => { 1308d82ecd9S猫头猫 if (draggingIndex !== undefined) { 1318d82ecd9S猫头猫 const time = lyric[draggingIndex].time + +(meta?.offset ?? 0); 1328d82ecd9S猫头猫 if (time !== undefined && !isNaN(time)) { 1338d82ecd9S猫头猫 await TrackPlayer.seekTo(time); 1348d82ecd9S猫头猫 await TrackPlayer.play(); 1358d82ecd9S猫头猫 setDraggingIndexImmi(undefined); 1368d82ecd9S猫头猫 } 1378d82ecd9S猫头猫 } 1388d82ecd9S猫头猫 }; 1398d82ecd9S猫头猫 140*a48a4bb0S猫头猫 console.log(draggingIndex, 'DD', currentLrcItem?.index); 1418d82ecd9S猫头猫 1428d82ecd9S猫头猫 return ( 1438d82ecd9S猫头猫 <View style={globalStyle.fwflex1}> 1448d82ecd9S猫头猫 {loading ? ( 1458d82ecd9S猫头猫 <Loading color="white" /> 1468d82ecd9S猫头猫 ) : lyric?.length ? ( 1478d82ecd9S猫头猫 <FlatList 1488d82ecd9S猫头猫 ref={_ => { 1498d82ecd9S猫头猫 listRef.current = _; 1508d82ecd9S猫头猫 }} 1518d82ecd9S猫头猫 onLayout={e => { 1528d82ecd9S猫头猫 setLayout(e.nativeEvent.layout); 1538d82ecd9S猫头猫 }} 1548d82ecd9S猫头猫 viewabilityConfig={{ 1558d82ecd9S猫头猫 itemVisiblePercentThreshold: 100, 1568d82ecd9S猫头猫 }} 1578d82ecd9S猫头猫 onScrollToIndexFailed={({index}) => { 1588d82ecd9S猫头猫 delay(120).then(() => { 1598d82ecd9S猫头猫 listRef.current?.scrollToIndex({ 1608d82ecd9S猫头猫 index: Math.min(index ?? 0, lyric.length - 1), 1618d82ecd9S猫头猫 viewPosition: 0.5, 1628d82ecd9S猫头猫 }); 1638d82ecd9S猫头猫 }); 1648d82ecd9S猫头猫 }} 1658d82ecd9S猫头猫 fadingEdgeLength={120} 1668d82ecd9S猫头猫 ListHeaderComponent={blankComponent} 1678d82ecd9S猫头猫 ListFooterComponent={blankComponent} 1688d82ecd9S猫头猫 onScrollBeginDrag={onScrollBeginDrag} 1698d82ecd9S猫头猫 onMomentumScrollEnd={onScrollEndDrag} 1708d82ecd9S猫头猫 onScroll={onScroll} 1718d82ecd9S猫头猫 scrollEventThrottle={32} 1728d82ecd9S猫头猫 style={style.wrapper} 1738d82ecd9S猫头猫 data={lyric} 1748d82ecd9S猫头猫 initialNumToRender={30} 1758d82ecd9S猫头猫 overScrollMode="never" 1768d82ecd9S猫头猫 extraData={currentLrcItem} 1778d82ecd9S猫头猫 renderItem={({item, index}) => ( 1788d82ecd9S猫头猫 <LyricItemComponent 1798d82ecd9S猫头猫 index={index} 1808d82ecd9S猫头猫 text={item.lrc} 1818d82ecd9S猫头猫 onLayout={handleLyricItemLayout} 1828d82ecd9S猫头猫 light={draggingIndex === index} 1838d82ecd9S猫头猫 highlight={currentLrcItem?.index === index} 1848d82ecd9S猫头猫 /> 1858d82ecd9S猫头猫 )} 1868d82ecd9S猫头猫 /> 1878d82ecd9S猫头猫 ) : ( 1888d82ecd9S猫头猫 <View style={globalStyle.fullCenter}> 1898d82ecd9S猫头猫 <Text style={style.white}>暂无歌词</Text> 1908d82ecd9S猫头猫 <TapGestureHandler 1918d82ecd9S猫头猫 onActivated={() => { 1928d82ecd9S猫头猫 showPanel('SearchLrc', { 1938d82ecd9S猫头猫 musicItem: TrackPlayer.getCurrentMusic(), 1948d82ecd9S猫头猫 }); 1958d82ecd9S猫头猫 }}> 1968d82ecd9S猫头猫 <Text style={style.searchLyric}>搜索歌词</Text> 1978d82ecd9S猫头猫 </TapGestureHandler> 1988d82ecd9S猫头猫 </View> 1998d82ecd9S猫头猫 )} 2008d82ecd9S猫头猫 {draggingIndex !== undefined && ( 2018d82ecd9S猫头猫 <View 2028d82ecd9S猫头猫 style={[ 2038d82ecd9S猫头猫 style.draggingTime, 2048d82ecd9S猫头猫 layout?.height 2058d82ecd9S猫头猫 ? { 2068d82ecd9S猫头猫 top: (layout.height - ITEM_HEIGHT) / 2, 2078d82ecd9S猫头猫 } 2088d82ecd9S猫头猫 : null, 2098d82ecd9S猫头猫 ]}> 2108d82ecd9S猫头猫 <DraggingTime 2118d82ecd9S猫头猫 time={ 2128d82ecd9S猫头猫 (lyric[draggingIndex]?.time ?? 0) + 2138d82ecd9S猫头猫 +(meta?.offset ?? 0) 2148d82ecd9S猫头猫 } 2158d82ecd9S猫头猫 /> 2168d82ecd9S猫头猫 <View style={style.singleLine} /> 2178d82ecd9S猫头猫 2188d82ecd9S猫头猫 <IconButtonWithGesture 2198d82ecd9S猫头猫 style={style.playIcon} 2208d82ecd9S猫头猫 sizeType="small" 2218d82ecd9S猫头猫 name="play" 2228d82ecd9S猫头猫 onPress={onLyricSeekPress} 2238d82ecd9S猫头猫 /> 2248d82ecd9S猫头猫 </View> 2258d82ecd9S猫头猫 )} 2268d82ecd9S猫头猫 </View> 2278d82ecd9S猫头猫 ); 2288d82ecd9S猫头猫} 2298d82ecd9S猫头猫 2308d82ecd9S猫头猫const style = StyleSheet.create({ 2318d82ecd9S猫头猫 wrapper: { 2328d82ecd9S猫头猫 width: '100%', 2338d82ecd9S猫头猫 marginVertical: rpx(48), 2348d82ecd9S猫头猫 flex: 1, 2358d82ecd9S猫头猫 }, 2368d82ecd9S猫头猫 empty: { 2378d82ecd9S猫头猫 paddingTop: '70%', 2388d82ecd9S猫头猫 }, 2398d82ecd9S猫头猫 white: { 2408d82ecd9S猫头猫 color: 'white', 2418d82ecd9S猫头猫 }, 2428d82ecd9S猫头猫 draggingTime: { 2438d82ecd9S猫头猫 position: 'absolute', 2448d82ecd9S猫头猫 width: '100%', 2458d82ecd9S猫头猫 height: ITEM_HEIGHT, 2468d82ecd9S猫头猫 top: '40%', 2478d82ecd9S猫头猫 marginTop: rpx(48), 2488d82ecd9S猫头猫 paddingHorizontal: rpx(18), 2498d82ecd9S猫头猫 right: 0, 2508d82ecd9S猫头猫 flexDirection: 'row', 2518d82ecd9S猫头猫 alignItems: 'center', 2528d82ecd9S猫头猫 justifyContent: 'space-between', 2538d82ecd9S猫头猫 }, 2548d82ecd9S猫头猫 draggingTimeText: { 2558d82ecd9S猫头猫 color: '#dddddd', 2568d82ecd9S猫头猫 fontSize: fontSizeConst.description, 2578d82ecd9S猫头猫 width: rpx(90), 2588d82ecd9S猫头猫 }, 2598d82ecd9S猫头猫 singleLine: { 2608d82ecd9S猫头猫 width: '67%', 2618d82ecd9S猫头猫 height: 1, 2628d82ecd9S猫头猫 backgroundColor: '#cccccc', 2638d82ecd9S猫头猫 opacity: 0.4, 2648d82ecd9S猫头猫 }, 2658d82ecd9S猫头猫 playIcon: { 2668d82ecd9S猫头猫 width: rpx(90), 2678d82ecd9S猫头猫 textAlign: 'right', 2688d82ecd9S猫头猫 color: 'white', 2698d82ecd9S猫头猫 }, 2708d82ecd9S猫头猫 searchLyric: { 2718d82ecd9S猫头猫 width: rpx(180), 2728d82ecd9S猫头猫 marginTop: rpx(14), 2738d82ecd9S猫头猫 paddingVertical: rpx(10), 2748d82ecd9S猫头猫 textAlign: 'center', 2758d82ecd9S猫头猫 alignSelf: 'center', 2768d82ecd9S猫头猫 color: '#66eeff', 2778d82ecd9S猫头猫 textDecorationLine: 'underline', 2788d82ecd9S猫头猫 }, 2798d82ecd9S猫头猫}); 280