diff options
Diffstat (limited to 'components/ParallaxScrollView.tsx')
-rw-r--r-- | components/ParallaxScrollView.tsx | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/components/ParallaxScrollView.tsx b/components/ParallaxScrollView.tsx new file mode 100644 index 0000000..5df1d75 --- /dev/null +++ b/components/ParallaxScrollView.tsx @@ -0,0 +1,82 @@ +import type { PropsWithChildren, ReactElement } from 'react'; +import { StyleSheet } from 'react-native'; +import Animated, { + interpolate, + useAnimatedRef, + useAnimatedStyle, + useScrollViewOffset, +} from 'react-native-reanimated'; + +import { ThemedView } from '@/components/ThemedView'; +import { useBottomTabOverflow } from '@/components/ui/TabBarBackground'; +import { useColorScheme } from '@/hooks/useColorScheme'; + +const HEADER_HEIGHT = 250; + +type Props = PropsWithChildren<{ + headerImage: ReactElement; + headerBackgroundColor: { dark: string; light: string }; +}>; + +export default function ParallaxScrollView({ + children, + headerImage, + headerBackgroundColor, +}: Props) { + const colorScheme = useColorScheme() ?? 'light'; + const scrollRef = useAnimatedRef<Animated.ScrollView>(); + const scrollOffset = useScrollViewOffset(scrollRef); + const bottom = useBottomTabOverflow(); + const headerAnimatedStyle = useAnimatedStyle(() => { + return { + transform: [ + { + translateY: interpolate( + scrollOffset.value, + [-HEADER_HEIGHT, 0, HEADER_HEIGHT], + [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] + ), + }, + { + scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]), + }, + ], + }; + }); + + return ( + <ThemedView style={styles.container}> + <Animated.ScrollView + ref={scrollRef} + scrollEventThrottle={16} + scrollIndicatorInsets={{ bottom }} + contentContainerStyle={{ paddingBottom: bottom }}> + <Animated.View + style={[ + styles.header, + { backgroundColor: headerBackgroundColor[colorScheme] }, + headerAnimatedStyle, + ]}> + {headerImage} + </Animated.View> + <ThemedView style={styles.content}>{children}</ThemedView> + </Animated.ScrollView> + </ThemedView> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + header: { + height: HEADER_HEIGHT, + overflow: 'hidden', + }, + content: { + flex: 1, + padding: 32, + gap: 16, + overflow: 'hidden', + }, +}); |