Refactor MetadataScreen to implement parallax scrolling effect; update scroll handling with useAnimatedScrollHandler for smoother transitions, enhance hero section with animated image, and adjust styles for improved visual dynamics. Replace ScrollView with Animated.ScrollView for better performance.

This commit is contained in:
tapframe 2025-05-02 16:16:51 +05:30
parent b46e491afa
commit dc19bdd253

View file

@ -12,6 +12,8 @@ import {
Dimensions, Dimensions,
Platform, Platform,
TouchableWithoutFeedback, TouchableWithoutFeedback,
NativeSyntheticEvent,
NativeScrollEvent,
} from 'react-native'; } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
import { useRoute, useNavigation, useFocusEffect } from '@react-navigation/native'; import { useRoute, useNavigation, useFocusEffect } from '@react-navigation/native';
@ -42,6 +44,7 @@ import Animated, {
FadeIn, FadeIn,
runOnJS, runOnJS,
Layout, Layout,
useAnimatedScrollHandler,
} from 'react-native-reanimated'; } from 'react-native-reanimated';
import { RouteProp } from '@react-navigation/native'; import { RouteProp } from '@react-navigation/native';
import { NavigationProp } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native';
@ -220,7 +223,8 @@ const MetadataScreen = () => {
// Get genres from context // Get genres from context
const { genreMap, loadingGenres } = useGenres(); const { genreMap, loadingGenres } = useGenres();
const contentRef = useRef<ScrollView>(null); // Update the ref type to be compatible with Animated.ScrollView
const contentRef = useRef<Animated.ScrollView>(null);
const [lastScrollTop, setLastScrollTop] = useState(0); const [lastScrollTop, setLastScrollTop] = useState(0);
const [isFullDescriptionOpen, setIsFullDescriptionOpen] = useState(false); const [isFullDescriptionOpen, setIsFullDescriptionOpen] = useState(false);
@ -254,6 +258,12 @@ const MetadataScreen = () => {
const logoOpacity = useSharedValue(0); const logoOpacity = useSharedValue(0);
const logoScale = useSharedValue(0.9); const logoScale = useSharedValue(0.9);
// Add shared value for parallax effect
const scrollY = useSharedValue(0);
// Create a dampened scroll value for smoother parallax
const dampedScrollY = useSharedValue(0);
// Debug log for route params // Debug log for route params
// logger.log('[MetadataScreen] Component mounted with route params:', { id, type, episodeId }); // logger.log('[MetadataScreen] Component mounted with route params:', { id, type, episodeId });
@ -643,14 +653,6 @@ const MetadataScreen = () => {
opacity: screenOpacity.value opacity: screenOpacity.value
})); }));
const heroAnimatedStyle = useAnimatedStyle(() => ({
width: '100%',
height: heroHeight.value,
backgroundColor: colors.black,
transform: [{ scale: heroScale.value }],
opacity: heroOpacity.value
}));
const contentAnimatedStyle = useAnimatedStyle(() => ({ const contentAnimatedStyle = useAnimatedStyle(() => ({
transform: [{ translateY: contentTranslateY.value }], transform: [{ translateY: contentTranslateY.value }],
opacity: interpolate( opacity: interpolate(
@ -847,6 +849,57 @@ const MetadataScreen = () => {
)); ));
}, [metadata?.genres]); // Dependency on metadata.genres }, [metadata?.genres]); // Dependency on metadata.genres
// Update the heroAnimatedStyle for parallax effect
const heroAnimatedStyle = useAnimatedStyle(() => ({
width: '100%',
height: heroHeight.value,
backgroundColor: colors.black,
transform: [{ scale: heroScale.value }],
opacity: heroOpacity.value,
}));
// Replace direct onScroll with useAnimatedScrollHandler
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
const rawScrollY = event.contentOffset.y;
scrollY.value = rawScrollY;
// Apply spring-like damping for smoother transitions
dampedScrollY.value = withTiming(rawScrollY, {
duration: 300,
easing: Easing.bezier(0.16, 1, 0.3, 1), // Custom spring-like curve
});
},
});
// Add a new animated style for the parallax image
const parallaxImageStyle = useAnimatedStyle(() => {
// Use dampedScrollY instead of direct scrollY for smoother effect
return {
width: '100%',
height: '120%', // Increase height for more movement range
top: '-10%', // Start image slightly higher to allow more upward movement
transform: [
{
translateY: interpolate(
dampedScrollY.value,
[0, 100, 300],
[20, -20, -60], // Start with a lower position, then move up
Extrapolate.CLAMP
)
},
{
scale: interpolate(
dampedScrollY.value,
[0, 150, 300],
[1.1, 1.02, 0.95], // More dramatic scale changes
Extrapolate.CLAMP
)
}
],
};
});
if (loading) { if (loading) {
return ( return (
<SafeAreaView <SafeAreaView
@ -931,23 +984,22 @@ const MetadataScreen = () => {
animated={true} animated={true}
/> />
<Animated.View style={containerAnimatedStyle}> <Animated.View style={containerAnimatedStyle}>
<ScrollView <Animated.ScrollView
ref={contentRef} ref={contentRef}
style={styles.scrollView} style={styles.scrollView}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
onScroll={(e) => { onScroll={scrollHandler}
// setLastScrollTop(e.nativeEvent.contentOffset.y); // Remove unused onScroll handler logic scrollEventThrottle={16} // Back to standard value
}}
scrollEventThrottle={16}
> >
{/* Hero Section */} {/* Hero Section */}
<Animated.View style={heroAnimatedStyle}> <Animated.View style={heroAnimatedStyle}>
<ImageBackground <View style={styles.heroSection}>
{/* Use Animated.Image directly instead of ImageBackground with imageStyle */}
<Animated.Image
source={{ uri: metadata.banner || metadata.poster }} source={{ uri: metadata.banner || metadata.poster }}
style={styles.heroSection} style={[styles.absoluteFill, parallaxImageStyle]}
imageStyle={styles.heroImage}
resizeMode="cover" resizeMode="cover"
> />
<LinearGradient <LinearGradient
colors={[ colors={[
`${colors.darkBackground}00`, `${colors.darkBackground}00`,
@ -1005,7 +1057,7 @@ const MetadataScreen = () => {
/> />
</View> </View>
</LinearGradient> </LinearGradient>
</ImageBackground> </View>
</Animated.View> </Animated.View>
{/* Main Content */} {/* Main Content */}
@ -1117,7 +1169,7 @@ const MetadataScreen = () => {
<MovieContent metadata={metadata} /> <MovieContent metadata={metadata} />
)} )}
</Animated.View> </Animated.View>
</ScrollView> </Animated.ScrollView>
</Animated.View> </Animated.View>
</SafeAreaView> </SafeAreaView>
); );
@ -1189,11 +1241,12 @@ const styles = StyleSheet.create({
backgroundColor: colors.black, backgroundColor: colors.black,
overflow: 'hidden', overflow: 'hidden',
}, },
heroImage: { absoluteFill: {
width: '100%', position: 'absolute',
height: '100%', top: 0,
top: '0%', left: 0,
transform: [{ scale: 1 }], right: 0,
bottom: 0,
}, },
heroGradient: { heroGradient: {
flex: 1, flex: 1,