Skip to content

Instantly share code, notes, and snippets.

@Stringsaeed
Created February 19, 2022 20:17
Show Gist options
  • Select an option

  • Save Stringsaeed/bc7dbea46cc97f433d3a797cfe7f7287 to your computer and use it in GitHub Desktop.

Select an option

Save Stringsaeed/bc7dbea46cc97f433d3a797cfe7f7287 to your computer and use it in GitHub Desktop.

Revisions

  1. Stringsaeed created this gist Feb 19, 2022.
    89 changes: 89 additions & 0 deletions AnimatedScrollHeader.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,89 @@
    // Animated Scroll Header Context
    // imports react and Animated
    import React, {createContext, useContext} from 'react';
    import {clamp} from 'react-native-redash';
    import {NativeScrollEvent, NativeSyntheticEvent, ViewStyle} from 'react-native';
    import Animated, {
    Extrapolate,
    interpolate,
    useAnimatedScrollHandler,
    useAnimatedStyle,
    useSharedValue,
    } from 'react-native-reanimated';

    // context type
    type AnimatedScrollHeaderContextType = {
    scrollY?: Animated.SharedValue<number>;
    onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
    headerStyle: Animated.AnimateStyle<ViewStyle>;
    };
    // provider props interface
    interface Props {
    children: React.ReactNode;
    headerHeight: number;
    }

    export interface AnimatedScrollHeaderProps extends Props {}

    // creates a context
    export const AnimatedScrollHeaderContext =
    createContext<AnimatedScrollHeaderContextType>({
    onScroll: () => {},
    headerStyle: {},
    });

    // creates a hook to access the context
    export const useAnimatedScrollHeader = () => {
    const context = useContext(AnimatedScrollHeaderContext);
    if (!context) {
    throw new Error(
    'useAnimatedScrollHeaderContext must be used within a AnimatedScrollHeaderContextProvider',
    );
    }
    return context;
    };

    // creates a provider
    export const AnimatedScrollHeaderProvider: React.FC<Props> = ({
    children,
    headerHeight,
    }) => {
    const scrollY = useSharedValue(0);

    const onScroll = useAnimatedScrollHandler<{prevY: number}>(
    {
    onScroll: (event, ctx) => {
    const diff = event.contentOffset.y - ctx.prevY;
    scrollY.value = clamp(scrollY.value + diff, 0, headerHeight);
    },
    onBeginDrag: (event, ctx) => {
    ctx.prevY = event.contentOffset.y;
    },
    },
    [],
    );

    const headerStyle = useAnimatedStyle(
    () => ({
    opacity: interpolate(scrollY.value, [0, 15], [1, 0], Extrapolate.CLAMP),
    transform: [
    {
    translateY: interpolate(
    scrollY.value,
    [0, 15],
    [0, -headerHeight],
    Extrapolate.CLAMP,
    ),
    },
    ],
    }),
    [headerHeight],
    );

    return (
    <AnimatedScrollHeaderContext.Provider
    value={{scrollY, onScroll, headerStyle}}>
    {children}
    </AnimatedScrollHeaderContext.Provider>
    );
    };