summaryrefslogtreecommitdiff
path: root/components/ParallaxScrollView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/ParallaxScrollView.tsx')
-rw-r--r--components/ParallaxScrollView.tsx82
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',
+ },
+});