react-native-reanimated#useCode TypeScript Examples

The following examples show how to use react-native-reanimated#useCode. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: layoutUtil.ts    From react-native-template with MIT License 6 votes vote down vote up
useDebug = (values: { [key: string]: Animated.Node<number> }) => {
  const keys = Object.keys(values)
  const nodes = Object.values(values)

  useCode(
    () =>
      call(nodes, (arrayOfNodes) => {
        keys.map((key, i) => console.log(key + " " + arrayOfNodes[i]))
      }),
    [keys]
  )
}
Example #2
Source File: CardModal.tsx    From react-native-template with MIT License 5 votes vote down vote up
CardModal = ({
  children,
  visible,
  cardHeight = layoutUtil.height,
  almostTuckedIn = false,
  cardStyle,
}: Props) => {
  const animation = useValue(0)
  const heightValue = useMemo(() => multiply(-cardHeight, HEIGHT_OFFSET), [
    cardHeight,
  ])

  const isTuckedIn = bin(visible)
  const isAlmostTuckedIn = bin(almostTuckedIn)

  useCode(
    () => [
      cond(
        // down animation
        or(not(isTuckedIn), isAlmostTuckedIn),
        set(
          animation,
          spring({
            to: cond(isAlmostTuckedIn, TUCK_IN_HEIGHT, 0),
            from: animation,
            config: {
              damping: new Value(20),
              mass: 0.6,
            },
          })
        ),
        cond(or(isTuckedIn, not(isAlmostTuckedIn)), [
          // up animation
          set(
            animation,
            spring({
              to: heightValue,
              from: animation,
              config: {
                damping: new Value(20),
                mass: 0.8,
              },
            })
          ),
        ])
      ),
    ],
    [visible, heightValue, almostTuckedIn]
  )

  return (
    <>
      <Animated.View
        style={[
          styles.card,
          {
            height: cardHeight * HEIGHT_OFFSET,
            bottom: -cardHeight * HEIGHT_OFFSET,
            transform: [{ translateY: animation }],
          },
          cardStyle,
        ]}
      >
        {children}
      </Animated.View>
    </>
  )
}
Example #3
Source File: PaperOnboarding.tsx    From react-native-paper-onboarding with MIT License 4 votes vote down vote up
PaperOnboardingComponent = forwardRef<
  PaperOnboarding,
  PaperOnboardingProps
>(
  (
    {
      data,
      safeInsets: _safeInsets,
      direction = DEFAULT_DIRECTION,
      // indicator config
      indicatorSize = DEFAULT_INDICATOR_SIZE,
      indicatorBackgroundColor = DEFAULT_INDICATOR_BACKGROUND_COLOR,
      indicatorBorderColor = DEFAULT_INDICATOR_BORDER_COLOR,
      // override styles
      titleStyle,
      descriptionStyle,
      // close button config
      closeButton,
      closeButtonTextStyle,
      closeButtonText = DEFAULT_CLOSE_BUTTON_TEXT,
      onCloseButtonPress = DEFAULT_CLOSE_BUTTON_CALLBACK,
      onIndexChange,
    },
    ref
  ) => {
    // state
    const [dimensions, setDimensions] =
      useState<PaperOnboardingScreenDimensions>({
        width: Dimensions.get('window').width,
        height: Dimensions.get('window').height,
      });

    // refs
    const indexRef = useRef<number>(0);
    const pagesRef = useRef<Array<Animated.View | null>>(data.map(() => null));

    //#region variables
    const safeInsets = useMemo<Required<Insets>>(() => {
      return {
        top: _safeInsets?.top ?? DEFAULT_SAFE_INSET,
        bottom: _safeInsets?.bottom ?? DEFAULT_SAFE_INSET,
        left: _safeInsets?.left ?? DEFAULT_SAFE_INSET,
        right: _safeInsets?.right ?? DEFAULT_SAFE_INSET,
      };
    }, [_safeInsets]);
    const indicatorsContainerLeftPadding = useMemo(() => {
      const containerLeftPadding = dimensions.width / 2 - indicatorSize / 2;
      return I18nManager.isRTL
        ? -containerLeftPadding + indicatorSize * (data.length - 1)
        : containerLeftPadding;
    }, [dimensions.width, indicatorSize, data.length]);
    //#endregion

    //#region animated variables
    const { gestureHandler, state, translation, velocity } =
      usePanGestureHandler();
    const animatedStaticIndex = useValue<number>(0);
    const animatedOverrideIndex = useValue<number>(0);
    const animatedIndex = useTiming({
      animatedStaticIndex,
      animatedOverrideIndex,
      value: direction === 'horizontal' ? translation.x : translation.y,
      velocity: direction === 'horizontal' ? velocity.x : velocity.y,
      state: state,
      size: data.length,
      screenWidth: dimensions.width,
    });
    const indicatorsContainerPosition = useMemo(
      () => data.map((_, index) => index * indicatorSize * -1),
      [data, indicatorSize]
    );
    const animatedIndicatorsContainerPosition = useMemo(
      () =>
        add(
          interpolate(animatedIndex, {
            inputRange: data.map((_, index) => index),
            outputRange: I18nManager.isRTL
              ? indicatorsContainerPosition.reverse()
              : indicatorsContainerPosition,
            extrapolate: Animated.Extrapolate.CLAMP,
          }),
          indicatorsContainerLeftPadding
        ),
      [
        data,
        animatedIndex,
        indicatorsContainerLeftPadding,
        indicatorsContainerPosition,
      ]
    );
    //#endregion

    //#region callbacks
    const handlePageRef = useCallback((pageRef, index) => {
      pagesRef.current[index] = pageRef;
    }, []);

    const handleOnLayout = useCallback(
      ({
        nativeEvent: {
          layout: { width, height },
        },
      }: LayoutChangeEvent) => {
        setDimensions({
          width,
          height,
        });
      },
      []
    );
    //#endregion

    //#region public methods
    const handleNavigateToNextPage = useCallback(() => {
      const currentIndex = indexRef.current;
      if (currentIndex === data.length - 1) {
        return;
      }
      animatedOverrideIndex.setValue(currentIndex + 1);
    }, [data, animatedOverrideIndex]);
    const handleNavigateToPreviousPage = useCallback(() => {
      const currentIndex = indexRef.current;
      if (currentIndex === 0) {
        return;
      }
      animatedOverrideIndex.setValue(currentIndex - 1);
    }, [animatedOverrideIndex]);
    useImperativeHandle(
      ref,
      () => ({
        next: handleNavigateToNextPage,
        previous: handleNavigateToPreviousPage,
      }),
      [handleNavigateToNextPage, handleNavigateToPreviousPage]
    );
    //#endregion

    //#region effects
    useCode(
      () =>
        onChange(
          animatedStaticIndex,
          call([animatedStaticIndex], args => {
            indexRef.current = args[0];
            /**
             * @DEV
             * here we directly manipulate pages native props by setting `pointerEvents`
             * to `auto` for current page and `none` for others.
             */
            pagesRef.current.map((pageRef, _index) => {
              // @ts-ignore
              pageRef.setNativeProps({
                pointerEvents: _index === args[0] ? 'auto' : 'none',
              });
            });

            if (onIndexChange) {
              onIndexChange(args[0]);
            }
          })
        ),
      []
    );
    //#endregion

    // renders
    return (
      <PanGestureHandler {...gestureHandler}>
        <Animated.View onLayout={handleOnLayout} style={styles.container}>
          <Background
            animatedIndex={animatedIndex}
            data={data}
            safeInsets={safeInsets}
            screenDimensions={dimensions}
            indicatorSize={indicatorSize}
            animatedIndicatorsContainerPosition={
              animatedIndicatorsContainerPosition
            }
          />

          {data.map((item, index) => (
            <Page
              key={`page-${index}`}
              index={index}
              item={item}
              animatedIndex={animatedIndex}
              indicatorSize={indicatorSize}
              titleStyle={titleStyle}
              descriptionStyle={descriptionStyle}
              safeInsets={safeInsets}
              screenDimensions={dimensions}
              handleRef={handlePageRef}
            />
          ))}

          <IndicatorsContainer
            data={data}
            animatedIndex={animatedIndex}
            animatedIndicatorsContainerPosition={
              animatedIndicatorsContainerPosition
            }
            indicatorSize={indicatorSize}
            indicatorBackgroundColor={indicatorBackgroundColor}
            indicatorBorderColor={indicatorBorderColor}
            safeInsets={safeInsets}
          />

          <CloseButton
            data={data}
            animatedIndex={animatedIndex}
            safeInsets={safeInsets}
            closeButtonText={closeButtonText}
            closeButtonTextStyle={closeButtonTextStyle}
            closeButton={closeButton}
            onCloseButtonPress={onCloseButtonPress}
          />
        </Animated.View>
      </PanGestureHandler>
    );
  }
)
Example #4
Source File: StickyItemFlatList.tsx    From react-native-sticky-item with MIT License 4 votes vote down vote up
StickyItemFlatList = forwardRef(
  <T extends {}>(props: StickyItemFlatListProps<T>, ref: Ref<FlatList<T>>) => {
    const {
      initialScrollIndex = 0,
      decelerationRate = DEFAULT_DECELERATION_RATE,
      itemWidth,
      itemHeight,
      separatorSize = DEFAULT_SEPARATOR_SIZE,
      borderRadius = DEFAULT_BORDER_RADIUS,
      stickyItemActiveOpacity = DEFAULT_STICKY_ITEM_ACTIVE_OPACITY,
      stickyItemWidth,
      stickyItemHeight,
      stickyItemBackgroundColors,
      stickyItemContent,
      onStickyItemPress,
      isRTL = DEFAULT_IS_RTL,
      ItemSeparatorComponent = Separator,
      ...rest
    } = props;

    // refs
    const flatListRef = useRef<FlatList<T>>(null);
    const tapRef = useRef<TapGestureHandler>(null);

    //#region variables
    const itemWidthWithSeparator = useMemo(
      () => itemWidth + separatorSize,
      [itemWidth, separatorSize]
    );
    const separatorProps = useMemo(
      () => ({
        size: separatorSize,
      }),
      [separatorSize]
    );
    //#endregion

    //#region styles
    const contentContainerStyle = useMemo(
      () => [
        rest.contentContainerStyle,
        {
          paddingLeft: itemWidth + separatorSize * 2,
          paddingRight: separatorSize,
        },
      ],
      [rest.contentContainerStyle, itemWidth, separatorSize]
    );
    //#endregion

    //#region methods
    const getHitSlop = useCallback(
      isMinimized => {
        const verticalPosition = isMinimized
          ? -((itemHeight - stickyItemHeight) / 2)
          : 0;
        const startPosition = isMinimized ? 0 : -separatorSize;
        const endPosition = isMinimized
          ? -(SCREEN_WIDTH - stickyItemWidth)
          : -(SCREEN_WIDTH - separatorSize - itemWidth);

        return {
          top: verticalPosition,
          right: isRTL ? startPosition : endPosition,
          left: isRTL ? endPosition : startPosition,
          bottom: verticalPosition,
        };
      },
      [
        itemWidth,
        itemHeight,
        stickyItemWidth,
        stickyItemHeight,
        separatorSize,
        isRTL,
      ]
    );
    const getItemLayout = useCallback(
      (_, index) => {
        return {
          length: itemWidthWithSeparator,
          // sticky item + previous items width
          offset: itemWidthWithSeparator + itemWidthWithSeparator * index,
          index,
        };
      },
      [itemWidthWithSeparator]
    );
    //#endregion

    //#region gesture
    const x = useValue(0);
    const tapState = useValue(State.UNDETERMINED);
    const tapGestures = useGestureHandler({ state: tapState });
    const onScroll = event([
      {
        nativeEvent: {
          contentOffset: {
            x,
          },
        },
      },
    ]);
    const onScrollEnd = event([
      {
        nativeEvent: {
          contentOffset: {
            x,
          },
        },
      },
    ]);
    //#endregion

    //#region effects
    //@ts-ignore
    useImperativeHandle(ref, () => flatListRef.current!.getNode());
    useCode(
      () =>
        cond(eq(tapState, State.END), [
          call([tapState], () => {
            if (onStickyItemPress) {
              onStickyItemPress();
            }
          }),
          set(tapState, State.UNDETERMINED),
        ]),
      [tapState]
    );
    useCode(
      () =>
        onChange(
          x,
          call([x], args => {
            if (tapRef.current) {
              const isMinimized = args[0] > 0;
              // @ts-ignore
              tapRef.current.setNativeProps({
                hitSlop: getHitSlop(isMinimized),
              });
            }
          })
        ),
      [
        x,
        itemWidth,
        itemHeight,
        stickyItemWidth,
        stickyItemWidth,
        separatorSize,
      ]
    );
    useEffect(() => {
      /**
       * @DEV
       * to fix stick item position with fast refresh
       */
      x.setValue(0);

      if (tapRef.current) {
        // @ts-ignore
        tapRef.current.setNativeProps({
          hitSlop: getHitSlop(initialScrollIndex !== 0),
        });
      }
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [getHitSlop]);
    //#endregion

    // render
    const renderSeparator = useCallback(() => {
      if (typeof ItemSeparatorComponent === 'function') {
        // @ts-ignore
        return ItemSeparatorComponent(separatorProps);
      } else {
        // @ts-ignore
        return <ItemSeparatorComponent size={separatorProps.size} />;
      }
    }, [ItemSeparatorComponent, separatorProps]);

    return (
      <TapGestureHandler
        ref={tapRef}
        waitFor={flatListRef}
        shouldCancelWhenOutside={true}
        {...tapGestures}
      >
        <Animated.View>
          <AnimatedFlatList
            {...rest}
            ref={flatListRef}
            initialScrollIndex={initialScrollIndex}
            inverted={isRTL}
            ItemSeparatorComponent={renderSeparator}
            contentContainerStyle={contentContainerStyle}
            horizontal={true}
            showsHorizontalScrollIndicator={false}
            scrollEventThrottle={1}
            pagingEnabled={true}
            decelerationRate={decelerationRate}
            snapToAlignment={'start'}
            snapToInterval={itemWidth + separatorSize}
            onScroll={onScroll}
            onScrollAnimationEnd={onScrollEnd}
            getItemLayout={getItemLayout}
          />
          <StickyItem
            x={x}
            tapState={tapState}
            itemWidth={itemWidth}
            itemHeight={itemHeight}
            separatorSize={separatorSize}
            borderRadius={borderRadius}
            stickyItemActiveOpacity={stickyItemActiveOpacity}
            stickyItemWidth={stickyItemWidth}
            stickyItemHeight={stickyItemHeight}
            stickyItemBackgroundColors={stickyItemBackgroundColors}
            stickyItemContent={stickyItemContent}
            isRTL={isRTL}
          />
        </Animated.View>
      </TapGestureHandler>
    );
  }
)
Example #5
Source File: Snackbar.tsx    From react-native-template with MIT License 4 votes vote down vote up
Snackbar = ({
  visible,
  message,
  onDismiss,
  btnTitle,
  onPress,
  unsafeView,
}: SnackbarProps) => {
  const timeoutRef = useRef(-1)
  const insets = useSafeArea()
  const safeArea = !unsafeView
    ? insets
    : { top: 0, bottom: 0, left: 0, right: 0 }
  const snackbarHeight =
    SNACKBAR_HEIGHT + safeArea.bottom + safeArea.bottom / 2 + 10
  const translateY = useValue(snackbarHeight)
  const opacity = useMemo(
    () =>
      timing({
        to: 1,
        from: 0.2,
        duration: 200,
      }),
    // eslint-disable-next-line
    [message]
  )

  useCode(
    () => [
      cond(
        bin(visible),
        set(
          translateY,
          timing({
            from: translateY,
            to: 0,
            duration: 250,
          })
        ),
        cond(
          neq(translateY, snackbarHeight),
          set(
            translateY,
            timing({
              from: translateY,
              to: snackbarHeight,
              duration: 150,
            })
          )
        )
      ),
    ],
    [visible, snackbarHeight, translateY]
  )

  useEffect(() => {
    if (visible) {
      timeoutRef.current = setTimeout(() => {
        onDismiss()
      }, 3000)
    }

    return clearTimeoutRef
  }, [onDismiss, visible])

  const clearTimeoutRef = () => clearTimeout(timeoutRef.current)

  const handleOnPress = () => {
    onDismiss()
    clearTimeout(timeoutRef.current)
    setTimeout(() => {
      onPress!()
    }, 150)
  }

  return (
    <View style={styles.container}>
      <Animated.View
        style={[
          styles.snackbar,
          {
            transform: [{ translateY }],
            height: snackbarHeight,
            backgroundColor: "#1D2226",
          },
        ]}
      >
        <Animated.Text
          style={[
            styles.text,
            {
              marginBottom: safeArea.bottom,
              opacity,
            },
          ]}
        >
          {message}
        </Animated.Text>
        {onPress && (
          <TouchableOpacity
            onPress={handleOnPress}
            style={styles.touchable}
            activeOpacity={0.6}
          >
            <Text
              style={[
                styles.dismissText,
                {
                  marginBottom: safeArea.bottom,
                  color: "#15AAE1",
                },
              ]}
            >
              {btnTitle}
            </Text>
          </TouchableOpacity>
        )}
        <TouchableOpacity
          onPress={onDismiss}
          style={styles.touchable}
          activeOpacity={0.6}
        >
          <Text
            style={[
              styles.dismissText,
              {
                marginBottom: safeArea.bottom,
                color: "#2E97C8",
              },
            ]}
          >
            Dismiss
          </Text>
        </TouchableOpacity>
      </Animated.View>
    </View>
  )
}