UNPKG

react-native-animated-header-flat-list

Version:

A React Native FlatList component with an animated collapsible header, featuring parallax effects, smooth title transitions, sticky component support, and customizable styles. Built with TypeScript and separate background/content layers in header.

125 lines (124 loc) 4.66 kB
"use strict"; import { useHeaderHeight } from '@react-navigation/elements'; import { useCallback, useState } from 'react'; import { useWindowDimensions } from 'react-native'; import { interpolate, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; export const useAnimatedHeaderFlatListAnimatedStyles = ({ headerTitleFontSize, navigationTitleFontSize, navigationTitleTranslateX = 0, navigationTitleTranslateY = 0 }) => { const { width: windowWidth } = useWindowDimensions(); const scrollY = useSharedValue(0); const navigationBarHeight = useHeaderHeight(); const safeAreaInsets = useSafeAreaInsets(); const [headerLayout, setHeaderLayout] = useState({ x: 0, y: 0, width: 0, height: 0 }); const [headerTitleLayout, setHeaderTitleLayout] = useState({ x: 0, y: 0, width: 0, height: 0 }); const [stickyComponentLayout, updateStickyComponentLayout] = useState({ x: 0, y: 0, width: 0, height: 0 }); const distanceBetweenTitleAndNavigationBar = (navigationBarHeight - safeAreaInsets.top + headerTitleLayout.height) / 2 + headerTitleLayout.y - navigationBarHeight; const navigationTitleOpacity = useSharedValue(0); const stickyHeaderOpacity = useSharedValue(0); const stickyComponentOpacity = useSharedValue(0); const setStickyComponentLayout = useCallback(layout => { updateStickyComponentLayout(layout); stickyComponentOpacity.value = layout.height > 0 ? 1 : 0; }, [updateStickyComponentLayout, stickyComponentOpacity]); const navigationBarAnimatedStyle = useAnimatedStyle(() => { return { opacity: interpolate(scrollY.value, [0, headerLayout.height - navigationBarHeight * 2], [0, 1], 'clamp'), marginBottom: Math.max(0, headerLayout.height - navigationBarHeight * 2 - scrollY.value), height: navigationBarHeight }; }); const navigationTitleAnimatedStyle = useAnimatedStyle(() => { return { opacity: navigationTitleOpacity.value, transform: [{ translateX: navigationTitleTranslateX }, { translateY: navigationTitleTranslateY }] }; }); const headerTitleAnimatedStyle = useAnimatedStyle(() => { return { opacity: 1 - navigationTitleOpacity.value, transform: [{ translateX: interpolate(scrollY.value, [0, distanceBetweenTitleAndNavigationBar], [0, windowWidth / 2 - headerTitleLayout.x - headerTitleLayout.width / 2 + navigationTitleTranslateX], 'clamp') }, { translateY: interpolate(scrollY.value, [0, distanceBetweenTitleAndNavigationBar], [0, navigationTitleTranslateY], 'clamp') }, { scale: interpolate(scrollY.value, [0, distanceBetweenTitleAndNavigationBar], [1, navigationTitleFontSize && headerTitleFontSize ? navigationTitleFontSize / headerTitleFontSize : 1], 'clamp') }] }; }); const stickyHeaderAnimatedStyle = useAnimatedStyle(() => { return { opacity: stickyHeaderOpacity.value }; }); const headerContentAnimatedStyle = useAnimatedStyle(() => { return { opacity: interpolate(scrollY.value, [0, headerLayout.height - navigationBarHeight * 2], [1, 0], 'clamp') }; }); const headerBackgroundAnimatedStyle = useAnimatedStyle(() => { if (scrollY.value >= 0) { return {}; } return { transform: [{ translateY: interpolate(scrollY.value, [scrollY.value, 0], [scrollY.value / 2, 0], 'clamp') }, { scale: interpolate(scrollY.value, [scrollY.value, 0], [1 - scrollY.value / (headerLayout.height - navigationBarHeight), 1], 'clamp') }] }; }); const stickyComponentAnimatedStyle = useAnimatedStyle(() => { return { opacity: stickyComponentOpacity.value }; }); const scrollHandler = useAnimatedScrollHandler(event => { scrollY.value = event.contentOffset.y; navigationTitleOpacity.value = event.contentOffset.y >= distanceBetweenTitleAndNavigationBar ? 1 : 0; stickyHeaderOpacity.value = event.contentOffset.y >= headerLayout.height - navigationBarHeight * 2 ? 1 : 0; }); return { scrollHandler, navigationBarHeight, headerLayout, setHeaderLayout, headerTitleLayout, setHeaderTitleLayout, stickyComponentLayout, setStickyComponentLayout, stickyComponentAnimatedStyle, navigationBarAnimatedStyle, navigationTitleAnimatedStyle, headerTitleAnimatedStyle, stickyHeaderAnimatedStyle, headerContentAnimatedStyle, headerBackgroundAnimatedStyle }; }; //# sourceMappingURL=useAnimatedHeaderFlatListAnimatedStyles.js.map