Lyrics implementation prototype (#224)
* Lyrics implementation prototype * feat: update lyrics view * chore: add docs * chore: cleanup * feat: animate active text * fix: hide lyrics button when there are none * feat: create lyrics preview in now playing modal * fix: header overlay color Closes #224 Closes #151 Closes #100 --------- Co-authored-by: Lei Nelissen <lei@codified.nl>
This commit is contained in:
committed by
GitHub
parent
a64f52c4f9
commit
c5b1406e16
86
src/screens/modals/Lyrics/components/LyricsProgress.tsx
Normal file
86
src/screens/modals/Lyrics/components/LyricsProgress.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import useDefaultStyles from '@/components/Colors';
|
||||
import ProgressTrack, { calculateProgressTranslation, ProgressTrackContainer } from '@/components/Progresstrack';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { LayoutChangeEvent } from 'react-native';
|
||||
import { useDerivedValue, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
||||
import { ViewProps } from 'react-native-svg/lib/typescript/fabric/utils';
|
||||
|
||||
export interface LyricsProgressProps extends Omit<ViewProps, 'onLayout'> {
|
||||
start: number;
|
||||
end: number;
|
||||
position: number;
|
||||
index: number;
|
||||
onActive: (index: number) => void;
|
||||
onLayout: (index: number, event: LayoutChangeEvent) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a loading bar when there is a silence in the lyrics.
|
||||
*/
|
||||
export default function LyricsProgress({
|
||||
start, end, position, index, onLayout, onActive, style, ...props
|
||||
}: LyricsProgressProps) {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
// Keep a reference to the width of the container
|
||||
const width = useSharedValue(0);
|
||||
|
||||
// Pass on layout changes to the parent
|
||||
const handleLayout = useCallback((e: LayoutChangeEvent) => {
|
||||
onLayout?.(index, e);
|
||||
width.value = e.nativeEvent.layout.width;
|
||||
}, [onLayout, index, width]);
|
||||
|
||||
// Determine whether the loader should be displayed
|
||||
const active = useMemo(() => (
|
||||
position > start && position < end
|
||||
), [start, end, position]);
|
||||
|
||||
// Call the parent when the active state changes
|
||||
useEffect(() => {
|
||||
if (active) onActive(index);
|
||||
}, [onActive, active, index]);
|
||||
|
||||
// Determine the duration of the progress bar
|
||||
const duration = useMemo(() => (end - start), [end, start]);
|
||||
|
||||
// Calculate the progress animation
|
||||
const progressAnimation = useDerivedValue(() => {
|
||||
// GUARD: If the animatino is not active, hide the progress bar
|
||||
if (!active) return -width.value;
|
||||
|
||||
// Calculate how far along we are
|
||||
const progress = calculateProgressTranslation(position - start, end - start, width.value);
|
||||
|
||||
// Move to that position with easing
|
||||
return withTiming(progress, { duration: 200 });
|
||||
});
|
||||
|
||||
// Calculate the styles according to the progress
|
||||
const progressStyles = useAnimatedStyle(() => {
|
||||
return {
|
||||
transform: [
|
||||
{ translateX: progressAnimation.value }
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
// GUARD: Only show durations if they last for more than 5 seconds.
|
||||
if (duration < 5e7) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ProgressTrackContainer
|
||||
{...props}
|
||||
style={[
|
||||
defaultStyles.trackBackground,
|
||||
{ flexGrow: 0, marginVertical: 8 },
|
||||
style
|
||||
]}
|
||||
onLayout={handleLayout}
|
||||
>
|
||||
<ProgressTrack style={[progressStyles, defaultStyles.themeBackground]} />
|
||||
</ProgressTrackContainer>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user