Files
OrionTV/components/ParallaxScrollView.tsx
Neil.X.Zhang 3b79d06b7d Update
2025-06-27 16:16:14 +08:00

88 lines
2.1 KiB
TypeScript

import type {PropsWithChildren, ReactElement} from 'react';
import {StyleSheet, useColorScheme} from 'react-native';
import Animated, {
interpolate,
useAnimatedRef,
useAnimatedStyle,
useScrollViewOffset,
} from 'react-native-reanimated';
import {ThemedView} from '@/components/ThemedView';
import {useScale} from '@/hooks/useScale';
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 scale = useScale();
const styles = useParallaxScrollViewStyles();
const HEADER_HEIGHT = 125 * scale;
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}>
<Animated.View
style={[
styles.header,
{backgroundColor: headerBackgroundColor[colorScheme]},
headerAnimatedStyle,
]}
>
{headerImage}
</Animated.View>
<ThemedView style={styles.content}>{children}</ThemedView>
</Animated.ScrollView>
</ThemedView>
);
}
const useParallaxScrollViewStyles = function () {
const scale = useScale();
return StyleSheet.create({
container: {
flex: 1,
},
header: {
height: 125 * scale,
overflow: 'hidden',
},
content: {
flex: 1,
padding: 32 * scale,
gap: 16 * scale,
overflow: 'hidden',
},
});
};