You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ParallaxScrollView.tsx 2.1KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import type { PropsWithChildren, ReactElement } from 'react';
  2. import { StyleSheet } from 'react-native';
  3. import Animated, {
  4. interpolate,
  5. useAnimatedRef,
  6. useAnimatedStyle,
  7. useScrollViewOffset,
  8. } from 'react-native-reanimated';
  9. import { ThemedView } from '@/components/ThemedView';
  10. import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
  11. import { useColorScheme } from '@/hooks/useColorScheme';
  12. const HEADER_HEIGHT = 250;
  13. type Props = PropsWithChildren<{
  14. headerImage: ReactElement;
  15. headerBackgroundColor: { dark: string; light: string };
  16. }>;
  17. export default function ParallaxScrollView({
  18. children,
  19. headerImage,
  20. headerBackgroundColor,
  21. }: Props) {
  22. const colorScheme = useColorScheme() ?? 'light';
  23. const scrollRef = useAnimatedRef<Animated.ScrollView>();
  24. const scrollOffset = useScrollViewOffset(scrollRef);
  25. const bottom = useBottomTabOverflow();
  26. const headerAnimatedStyle = useAnimatedStyle(() => {
  27. return {
  28. transform: [
  29. {
  30. translateY: interpolate(
  31. scrollOffset.value,
  32. [-HEADER_HEIGHT, 0, HEADER_HEIGHT],
  33. [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75]
  34. ),
  35. },
  36. {
  37. scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
  38. },
  39. ],
  40. };
  41. });
  42. return (
  43. <ThemedView style={styles.container}>
  44. <Animated.ScrollView
  45. ref={scrollRef}
  46. scrollEventThrottle={16}
  47. scrollIndicatorInsets={{ bottom }}
  48. contentContainerStyle={{ paddingBottom: bottom }}>
  49. <Animated.View
  50. style={[
  51. styles.header,
  52. { backgroundColor: headerBackgroundColor[colorScheme] },
  53. headerAnimatedStyle,
  54. ]}>
  55. {headerImage}
  56. </Animated.View>
  57. <ThemedView style={styles.content}>{children}</ThemedView>
  58. </Animated.ScrollView>
  59. </ThemedView>
  60. );
  61. }
  62. const styles = StyleSheet.create({
  63. container: {
  64. flex: 1,
  65. },
  66. header: {
  67. height: HEADER_HEIGHT,
  68. overflow: 'hidden',
  69. },
  70. content: {
  71. flex: 1,
  72. padding: 32,
  73. gap: 16,
  74. overflow: 'hidden',
  75. },
  76. });