diff --git a/src/screens/MetadataScreen.tsx b/src/screens/MetadataScreen.tsx index 9fc5ec6f..a8848068 100644 --- a/src/screens/MetadataScreen.tsx +++ b/src/screens/MetadataScreen.tsx @@ -12,6 +12,8 @@ import { Dimensions, Platform, TouchableWithoutFeedback, + NativeSyntheticEvent, + NativeScrollEvent, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useRoute, useNavigation, useFocusEffect } from '@react-navigation/native'; @@ -42,6 +44,7 @@ import Animated, { FadeIn, runOnJS, Layout, + useAnimatedScrollHandler, } from 'react-native-reanimated'; import { RouteProp } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native'; @@ -220,7 +223,8 @@ const MetadataScreen = () => { // Get genres from context const { genreMap, loadingGenres } = useGenres(); - const contentRef = useRef(null); + // Update the ref type to be compatible with Animated.ScrollView + const contentRef = useRef(null); const [lastScrollTop, setLastScrollTop] = useState(0); const [isFullDescriptionOpen, setIsFullDescriptionOpen] = useState(false); @@ -254,6 +258,12 @@ const MetadataScreen = () => { const logoOpacity = useSharedValue(0); 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 // logger.log('[MetadataScreen] Component mounted with route params:', { id, type, episodeId }); @@ -643,14 +653,6 @@ const MetadataScreen = () => { opacity: screenOpacity.value })); - const heroAnimatedStyle = useAnimatedStyle(() => ({ - width: '100%', - height: heroHeight.value, - backgroundColor: colors.black, - transform: [{ scale: heroScale.value }], - opacity: heroOpacity.value - })); - const contentAnimatedStyle = useAnimatedStyle(() => ({ transform: [{ translateY: contentTranslateY.value }], opacity: interpolate( @@ -847,6 +849,57 @@ const MetadataScreen = () => { )); }, [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) { return ( { animated={true} /> - { - // setLastScrollTop(e.nativeEvent.contentOffset.y); // Remove unused onScroll handler logic - }} - scrollEventThrottle={16} + onScroll={scrollHandler} + scrollEventThrottle={16} // Back to standard value > {/* Hero Section */} - + + {/* Use Animated.Image directly instead of ImageBackground with imageStyle */} + { /> - + {/* Main Content */} @@ -1117,7 +1169,7 @@ const MetadataScreen = () => { )} - + ); @@ -1189,11 +1241,12 @@ const styles = StyleSheet.create({ backgroundColor: colors.black, overflow: 'hidden', }, - heroImage: { - width: '100%', - height: '100%', - top: '0%', - transform: [{ scale: 1 }], + absoluteFill: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, }, heroGradient: { flex: 1,