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
72
src/screens/modals/Lyrics/components/LyricsLine.tsx
Normal file
72
src/screens/modals/Lyrics/components/LyricsLine.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import React, { memo, useCallback, useEffect, useMemo } from 'react';
|
||||
import useDefaultStyles from '@/components/Colors';
|
||||
import {LayoutChangeEvent, StyleProp, TextStyle, ViewProps} from 'react-native';
|
||||
import styled from 'styled-components/native';
|
||||
import Animated, { useAnimatedStyle, useDerivedValue, withTiming } from 'react-native-reanimated';
|
||||
|
||||
const Container = styled(Animated.View)`
|
||||
|
||||
`;
|
||||
|
||||
const LyricsText = styled(Animated.Text)`
|
||||
flex: 1;
|
||||
font-size: 24px;
|
||||
`;
|
||||
|
||||
export interface LyricsLineProps extends Omit<ViewProps, 'onLayout'> {
|
||||
text?: string;
|
||||
start: number;
|
||||
end: number;
|
||||
position: number;
|
||||
index: number;
|
||||
onActive: (index: number) => void;
|
||||
onLayout: (index: number, event: LayoutChangeEvent) => void;
|
||||
size: 'small' | 'full';
|
||||
}
|
||||
|
||||
/**
|
||||
* A single lyric line
|
||||
*/
|
||||
function LyricsLine({
|
||||
text, start, end, position, size, onLayout, onActive, index, ...viewProps
|
||||
}: LyricsLineProps) {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
|
||||
// Pass on layout changes to the parent
|
||||
const handleLayout = useCallback((e: LayoutChangeEvent) => {
|
||||
onLayout?.(index, e);
|
||||
}, [onLayout, index]);
|
||||
|
||||
// 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 current style for this line
|
||||
const lyricsTextStyle: StyleProp<TextStyle> = useMemo(() => ({
|
||||
color: active ? defaultStyles.themeColor.color : defaultStyles.text.color,
|
||||
opacity: active ? 1 : 0.7,
|
||||
transformOrigin: 'left center',
|
||||
fontSize: size === 'full' ? 24 : 18,
|
||||
}), [active, defaultStyles, size]);
|
||||
|
||||
const scale = useDerivedValue(() => withTiming(active ? 1.05 : 1));
|
||||
const animatedStyle = useAnimatedStyle(() => ({
|
||||
transform: [{ scale: scale.value }],
|
||||
}));
|
||||
|
||||
return (
|
||||
<Container {...viewProps} onLayout={handleLayout} >
|
||||
<LyricsText style={[lyricsTextStyle, animatedStyle]}>
|
||||
{text}
|
||||
</LyricsText>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(LyricsLine);
|
||||
Reference in New Issue
Block a user