react-native-reanimated#timing TypeScript Examples

The following examples show how to use react-native-reanimated#timing. 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: index.tsx    From react-native-floating-label-input with MIT License 4 votes vote down vote up
FloatingLabelInput: React.ForwardRefRenderFunction<InputRef, Props> = (
  {
    label,
    labelProps,
    mask,
    isPassword,
    maxLength,
    inputStyles,
    showCountdown,
    showCountdownStyles,
    labelStyles,
    darkTheme,
    countdownLabel,
    currencyDivider,
    currency,
    maskType,
    onChangeText,
    customHidePasswordComponent,
    customShowPasswordComponent,
    isFocused,
    onBlur,
    onFocus,
    onTogglePassword,
    togglePassword,
    leftComponent,
    rightComponent,
    customHidePasswordImage,
    customLabelStyles = {},
    staticLabel = false,
    hint,
    hintTextColor,
    onSubmit,
    containerStyles,
    customShowPasswordImage,
    showPasswordContainerStyles,
    maxDecimalPlaces,
    multiline,
    showPasswordImageStyles,
    value = '',
    animationDuration,
    ...rest
  }: Props,
  ref: any,
) => {
  const [halfTop, setHalfTop] = useState(0);
  const [isFocusedState, setIsFocused] = useState(false);
  const [secureText, setSecureText] = useState(true);
  const inputRef = useRef<any>(null);

  customLabelStyles = {
    fontSizeFocused: 10,
    fontSizeBlurred: 14,
    colorFocused: '#49658c',
    colorBlurred: '#49658c',
    ...setGlobalStyles?.customLabelStyles,
    ...customLabelStyles,
  };

  const [fontColorAnimated] = useState(new Animated.Value(0));

  const [fontSizeAnimated] = useState(
    new Animated.Value(
      isFocused
        ? customLabelStyles.fontSizeFocused
          ? customLabelStyles.fontSizeFocused
          : 10
        : customLabelStyles.fontSizeBlurred
        ? customLabelStyles.fontSizeBlurred
        : 14,
    ),
  );

  const [leftAnimated] = useState(
    new Animated.Value(
      staticLabel
        ? customLabelStyles?.leftFocused !== undefined
          ? customLabelStyles.leftFocused
          : 15
        : customLabelStyles != undefined &&
          customLabelStyles.leftBlurred !== undefined
        ? customLabelStyles.leftBlurred
        : 0,
    ),
  );

  const [topAnimated] = useState(
    new Animated.Value(
      staticLabel
        ? customLabelStyles?.topFocused !== undefined
          ? customLabelStyles.topFocused
          : 0
        : customLabelStyles.topBlurred
        ? customLabelStyles.topBlurred
        : 0,
    ),
  );

  useEffect(() => {
    if (isFocused === undefined) {
      if (value !== '' || isFocusedState) {
        setIsFocused(true);
      } else if (value === '' || value === null) {
        setIsFocused(false);
      }
    }
  }, [value]);

  useEffect(() => {
    if (isFocused !== undefined) {
      if (value !== '' || isFocused) {
        setIsFocused(true);
      } else {
        setIsFocused(false);
      }
    }
  }, [isFocused, value]);

  useEffect(() => {
    if (togglePassword !== undefined) {
      _toggleVisibility(togglePassword);
    }
  }, [togglePassword]);

  useEffect(() => {
    if (isFocusedState || value !== '') {
      if (halfTop !== 0) {
        animateFocus();
      }
    } else {
      animateBlur();
    }
  }, [isFocusedState, halfTop]);

  useImperativeHandle(ref, () => ({
    focus() {
      inputRef.current.focus();
    },
    blur() {
      inputRef.current.blur();
    },
  }));

  useEffect(() => {
    if (
      !staticLabel &&
      customLabelStyles.topFocused === undefined &&
      isFocusedState
    ) {
      ReactAnimated.parallel([
        timing(leftAnimated, {
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
          toValue: customLabelStyles.leftFocused
            ? customLabelStyles.leftFocused
            : 0,
        }),
        timing(fontSizeAnimated, {
          toValue: customLabelStyles.fontSizeFocused
            ? customLabelStyles.fontSizeFocused
            : 10,
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
        timing(topAnimated, {
          toValue: customLabelStyles.topFocused
            ? customLabelStyles.topFocused
            : -halfTop +
              (isFocusedState
                ? customLabelStyles.fontSizeFocused
                  ? customLabelStyles.fontSizeFocused
                  : 10
                : customLabelStyles.fontSizeBlurred
                ? customLabelStyles.fontSizeBlurred
                : 14),
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
        // @ts-ignore
        timing(fontColorAnimated, {
          toValue: 1,
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
      ]).start();
    } else if (staticLabel && isFocusedState) {
      Animated.timing(fontColorAnimated, {
        toValue: 1,
        duration: animationDuration ? animationDuration : 300,
        easing: EasingNode.linear,
      }).start();
    }
  }, [halfTop]);

  function animateFocus() {
    if (!staticLabel) {
      ReactAnimated.parallel([
        timing(leftAnimated, {
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
          toValue: customLabelStyles.leftFocused
            ? customLabelStyles.leftFocused
            : 0,
        }),
        timing(fontSizeAnimated, {
          toValue: customLabelStyles.fontSizeFocused
            ? customLabelStyles.fontSizeFocused
            : 10,
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
        timing(topAnimated, {
          toValue: customLabelStyles.topFocused
            ? customLabelStyles.topFocused
            : -halfTop +
              (isFocusedState
                ? customLabelStyles.fontSizeFocused
                  ? customLabelStyles.fontSizeFocused
                  : 10
                : customLabelStyles.fontSizeBlurred
                ? customLabelStyles.fontSizeBlurred
                : 14),
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
        // @ts-ignore
        timing(fontColorAnimated, {
          toValue: 1,
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
      ]).start();
    } else {
      Animated.timing(fontColorAnimated, {
        toValue: 1,
        duration: animationDuration ? animationDuration : 300,
        easing: EasingNode.linear,
      }).start();
    }
  }

  function animateBlur() {
    if (!staticLabel) {
      ReactAnimated.parallel([
        timing(leftAnimated, {
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
          toValue: customLabelStyles.leftBlurred
            ? customLabelStyles.leftBlurred
            : 0,
        }),
        timing(fontSizeAnimated, {
          toValue: customLabelStyles.fontSizeBlurred
            ? customLabelStyles.fontSizeBlurred
            : 14,
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
        timing(topAnimated, {
          toValue: customLabelStyles.topBlurred
            ? customLabelStyles.topBlurred
            : 0,
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
        // @ts-ignore
        timing(fontColorAnimated, {
          toValue: 0,
          duration: animationDuration ? animationDuration : 300,
          easing: EasingNode.linear,
        }),
      ]).start();
    } else {
      Animated.timing(fontColorAnimated, {
        toValue: 0,
        duration: animationDuration ? animationDuration : 300,
        easing: EasingNode.linear,
      }).start();
    }
  }

  function handleFocus() {
    setIsFocused(true);
  }

  function handleBlur() {
    if (value === '') {
      setIsFocused(false);
    }
  }

  function setFocus() {
    inputRef.current?.focus();
  }

  function setBlur() {
    inputRef.current?.blur();
  }

  function _toggleVisibility(toggle?: boolean) {
    if (toggle === undefined) {
      if (onTogglePassword) {
        onTogglePassword(!secureText);
      }
      setSecureText(!secureText);
      secureText ? setFocus() : setBlur();
    } else {
      if (!((secureText && !toggle) || (!secureText && toggle))) {
        if (onTogglePassword) {
          onTogglePassword(!toggle);
        }
        setSecureText(!toggle);
        toggle ? setFocus() : setBlur();
      }
    }
  }

  function onSubmitEditing() {
    if (onSubmit !== undefined) {
      onSubmit();
    }
  }

  const imgSource = darkTheme
    ? secureText
      ? customShowPasswordImage || makeVisibleBlack
      : customHidePasswordImage || makeInvisibleBlack
    : secureText
    ? customShowPasswordImage || makeVisibleWhite
    : customHidePasswordImage || makeInvisibleWhite;

  const style: TextStyle = {
    ...setGlobalStyles?.labelStyles,
    ...labelStyles,
    left: labelStyles?.left !== undefined ? labelStyles?.left : 5,
    fontSize: staticLabel
      ? customLabelStyles?.fontSizeFocused !== undefined
        ? customLabelStyles.fontSizeFocused
        : 10
      : !isFocusedState
      ? customLabelStyles.fontSizeBlurred
      : customLabelStyles.fontSizeFocused,
    // @ts-ignore
    color: interpolateColors(fontColorAnimated, {
      inputRange: [0, 1],
      outputColorRange: [
        // @ts-ignore
        customLabelStyles.colorBlurred,
        // @ts-ignore
        customLabelStyles.colorFocused,
      ],
    }),
    alignSelf: 'center',
    position: 'absolute',
    flex: 1,
    zIndex: 999,
  };

  let input: TextStyle =
    inputStyles !== undefined
      ? inputStyles
      : setGlobalStyles?.inputStyles !== undefined
      ? setGlobalStyles.inputStyles
      : styles.input;

  input = {
    ...input,
    flex: 1,
    color:
      input.color !== undefined ? input.color : customLabelStyles.colorFocused,
    zIndex: style?.zIndex !== undefined ? style.zIndex - 2 : 0,
  };

  containerStyles =
    containerStyles !== undefined
      ? containerStyles
      : setGlobalStyles?.containerStyles !== undefined
      ? setGlobalStyles?.containerStyles
      : styles.container;

  containerStyles = {
    ...containerStyles,
    alignItems: 'center',
    flexDirection: 'row',
    flex: 1,
    zIndex: style?.zIndex !== undefined ? style.zIndex - 6 : 0,
  };

  let toggleButton =
    showPasswordContainerStyles !== undefined
      ? showPasswordContainerStyles
      : setGlobalStyles?.showPasswordContainerStyles !== undefined
      ? setGlobalStyles.showPasswordContainerStyles
      : styles.toggleButton;

  toggleButton = {
    ...toggleButton,
    alignSelf: 'center',
  };

  let img =
    showPasswordImageStyles !== undefined
      ? showPasswordImageStyles
      : setGlobalStyles?.showPasswordImageStyles !== undefined
      ? setGlobalStyles.showPasswordImageStyles
      : styles.img;

  img = {
    height: 25,
    width: 25,
    ...img,
  };

  const countdown = {
    ...styles.countdown,
    ...setGlobalStyles?.showCountdownStyles,
    ...showCountdownStyles,
  };

  function onChangeTextCallback(val: string): void | undefined {
    if (onChangeText === undefined) return undefined;

    if (maskType === undefined && mask === undefined) return onChangeText(val);

    let newValue: string | undefined;

    if (maskType !== 'currency' && mask !== undefined) {
      newValue = getValueWithNonCurrencyMask({ value: val, mask });
    }

    if (maskType === 'currency') {
      if (
        currency !== undefined &&
        !/^[0-9]+$/g.test(val.replace(/[.,]/g, '').replace(currency, '')) &&
        val.replace(/[.,]/g, '').replace(currency, '') !== ''
      ) {
        return undefined;
      } else if (
        currency === undefined &&
        !/^[0-9]+$/g.test(val.replace(/[.,]/g, '')) &&
        val.replace(/[.,]/g, '') !== ''
      ) {
        return undefined;
      }

      newValue = getValueWithCurrencyMask({
        value: currency !== undefined ? value.replace(currency, '') : value,
        newValue: currency !== undefined ? val.replace(currency, '') : val,
        currencyDivider,
        maxDecimalPlaces,
      });
    }

    if (newValue !== undefined) {
      return onChangeText((currency !== undefined ? currency : '') + newValue);
    }
  }

  function onLayout(event: LayoutChangeEvent) {
    const { height } = event.nativeEvent.layout;
    setHalfTop(height / 2);
  }

  return (
    <TouchableWithoutFeedback onPress={setFocus} onLayout={onLayout}>
      <View style={{ flexDirection: 'row' }}>
        {staticLabel && (
          <AnimatedText
            {...labelProps}
            onPress={setFocus}
            style={[
              style,
              {
                left: labelStyles?.left
                  ? labelStyles?.left
                  : customLabelStyles.leftFocused
                  ? customLabelStyles.leftFocused
                  : 20,
                top: -(style?.fontSize ? style?.fontSize : 10) / 2,
              },
            ]}
          >
            {label}
          </AnimatedText>
        )}
        <View style={containerStyles}>
          {leftComponent && leftComponent}
          <View style={{ flex: 1, flexDirection: 'row' }}>
            {!staticLabel && (
              <AnimatedText
                {...labelProps}
                onPress={setFocus}
                style={[
                  style,
                  // @ts-ignore
                  {
                    fontSize: fontSizeAnimated,
                    transform: [
                      { translateX: leftAnimated },
                      { translateY: topAnimated },
                    ],
                  },
                ]}
              >
                {label}
              </AnimatedText>
            )}
            <TextInput
              value={value}
              onSubmitEditing={onSubmitEditing}
              secureTextEntry={
                isPassword !== undefined ? isPassword && secureText : false
              }
              onFocus={onFocus !== undefined ? onFocus : handleFocus}
              onBlur={onBlur !== undefined ? onBlur : handleBlur}
              ref={inputRef}
              {...rest}
              maxLength={
                mask !== undefined
                  ? mask.length
                  : maxLength !== undefined
                  ? maxLength
                  : undefined
              }
              placeholderTextColor={hintTextColor}
              placeholder={(staticLabel || isFocusedState) && hint ? hint : ''}
              multiline={multiline}
              onChangeText={onChangeTextCallback}
              style={input}
            />
            {rightComponent && rightComponent}
            {isPassword && (
              <TouchableOpacity
                style={toggleButton}
                onPress={() => _toggleVisibility()}
              >
                {secureText && customShowPasswordComponent !== undefined ? (
                  customShowPasswordComponent
                ) : !secureText && customHidePasswordComponent !== undefined ? (
                  customHidePasswordComponent
                ) : (
                  <Image source={imgSource} resizeMode="contain" style={img} />
                )}
              </TouchableOpacity>
            )}
          </View>
          {showCountdown && maxLength && (
            <Text style={countdown}>
              {maxLength - (value ? value.length : 0)} {countdownLabel}
            </Text>
          )}
        </View>
      </View>
    </TouchableWithoutFeedback>
  );
}
Example #2
Source File: AuthForm.tsx    From react-native-crypto-wallet-app with MIT License 4 votes vote down vote up
AuthForm: React.FC<IAuthFormProps> = ({
  isSignUp,
  email,
  isEmailValid,
  password,
  isPasswordValid,
  loading,
  isButtonDisabled,
  showPassword,
  submitButtonLabel,
  bottomSectionLightTextLabel,
  bottomSectionAccentTextLabel,
  onEmailChange,
  onPasswordChange,
  onShowPasswordPress,
  onSignUpPress,
  onForgotPasswordPress,
  onNavigationToLoginOrSignUp,
}) => {
  const keyboardDidShow = useKeyboardDidShow();
  const { height } = Dimensions.get('window');
  const UNFOCUSED_HEIGHT = (height * 57) / 100;
  const FOCUSED_HEIGHT = (height * 87) / 100;
  const containerInitialHeight = useValue(UNFOCUSED_HEIGHT);

  const containerAnimConfig = {
    duration: 200,
    easing: Easing.inOut(Easing.ease),
  };

  useEffect(() => {
    if (keyboardDidShow) {
      timing(containerInitialHeight, { ...containerAnimConfig, toValue: FOCUSED_HEIGHT }).start();
    } else {
      timing(containerInitialHeight, { ...containerAnimConfig, toValue: UNFOCUSED_HEIGHT }).start();
    }
  }, [
    FOCUSED_HEIGHT,
    UNFOCUSED_HEIGHT,
    containerAnimConfig,
    containerInitialHeight,
    keyboardDidShow,
  ]);

  return (
    <ContentContainer height={containerInitialHeight}>
      <StyledInput
        label="Email Address"
        value={email}
        onChangeText={onEmailChange}
        keyboardType="email-address"
        disabled={loading}
        errorText={isEmailValid === undefined || isEmailValid ? '' : 'Email address is not valid'}
        ariaLabel="email"
      />
      <StyledInput
        {...{ showPassword }}
        label="Password"
        value={password}
        onChangeText={onPasswordChange}
        onShowPasswordPress={onShowPasswordPress}
        disabled={loading}
        errorText={isPasswordValid === undefined || isPasswordValid ? '' : 'Password is not valid'}
        isPassword
        ariaLabel="password"
      />
      {!isSignUp && (
        <Box alignSelf="flex-end">
          <PressableText
            variant="link"
            label="Forgot your password?"
            onPress={onForgotPasswordPress!}
          />
        </Box>
      )}
      <View style={AuthFormStyle.bottomSection}>
        <BottomSection
          mainButtonVariant="primary"
          mainButtonLabel={submitButtonLabel}
          mainButtonLoading={loading}
          mainButtonDisabled={isButtonDisabled}
          lightTextLabel={bottomSectionLightTextLabel}
          accentTextLabel={bottomSectionAccentTextLabel}
          onMainButtonPress={onSignUpPress}
          onAccentTextPress={onNavigationToLoginOrSignUp}
        />
      </View>
    </ContentContainer>
  );
}
Example #3
Source File: useTiming.ts    From react-native-paper-onboarding with MIT License 4 votes vote down vote up
useTiming = ({
  animatedStaticIndex,
  animatedOverrideIndex,
  value,
  velocity,
  state,
  size,
  screenWidth,
}: useTimingProps) => {
  const clock = useClock();
  const isManuallyAnimated = useValue(0);

  const config = useMemo(
    () => ({
      toValue: new Animated.Value(0),
      duration: 500,
      easing: Easing.out(Easing.exp),
    }),
    []
  );

  const animationState = useMemo(
    () => ({
      finished: new Animated.Value(0),
      position: new Animated.Value(0),
      frameTime: new Animated.Value(0),
      time: new Animated.Value(0),
    }),
    []
  );

  const valueClamp = useMemo(
    () =>
      interpolate(value, {
        inputRange: [screenWidth * -1, 0, screenWidth],
        outputRange: I18nManager.isRTL ? [-1, 0, 1] : [1, 0, -1],
        extrapolate: Animated.Extrapolate.CLAMP,
      }),
    [value, screenWidth]
  );

  const velocityClamp = useMemo(
    () =>
      interpolate(velocity, {
        inputRange: [screenWidth * -2, 0, screenWidth * 2],
        outputRange: I18nManager.isRTL ? [-0.5, 0, 0.5] : [0.5, 0, -0.5],
        extrapolate: Animated.Extrapolate.CLAMP,
      }),
    [screenWidth, velocity]
  );

  const isTimingInterrupted = and(eq(state, State.BEGAN), clockRunning(clock));
  const finishTiming = useMemo(
    () => [
      set(animatedStaticIndex, config.toValue),
      set(animatedOverrideIndex, config.toValue),
      set(animationState.frameTime, 0),
      set(animationState.time, 0),
      set(state, State.UNDETERMINED),
      set(isManuallyAnimated, 0),
      stopClock(clock),
    ],
    [
      state,
      animatedOverrideIndex,
      animatedStaticIndex,
      animationState.frameTime,
      animationState.time,
      clock,
      config.toValue,
      isManuallyAnimated,
    ]
  );

  const shouldAnimate = useMemo(
    () =>
      and(
        not(and(eq(animatedStaticIndex, 0), lessThan(valueClamp, 0))),
        not(and(eq(animatedStaticIndex, size - 1), greaterThan(valueClamp, 0)))
      ),
    [animatedStaticIndex, size, valueClamp]
  );
  const shouldReset = useMemo(
    () => not(greaterThan(add(abs(valueClamp), abs(velocityClamp)), 0.5)),
    [valueClamp, velocityClamp]
  );
  const shouldAnimateNext = useMemo(
    () =>
      greaterThan(
        add(animationState.position, velocityClamp),
        animatedStaticIndex
      ),
    [animatedStaticIndex, animationState.position, velocityClamp]
  );

  const animatedPosition = useMemo(
    () =>
      block([
        cond(isTimingInterrupted, finishTiming),
        cond(
          eq(state, State.ACTIVE),
          cond(
            and(
              not(and(eq(animatedStaticIndex, 0), lessThan(valueClamp, 0))),
              not(
                and(
                  eq(animatedStaticIndex, size - 1),
                  greaterThan(valueClamp, 0)
                )
              )
            ),
            [
              set(animationState.finished, 0),
              set(
                animationState.position,
                add(animatedStaticIndex, valueClamp)
              ),
            ]
          )
        ),

        onChange(animatedOverrideIndex, [
          set(isManuallyAnimated, 1),
          set(animationState.finished, 0),
        ]),

        cond(or(eq(state, State.END), isManuallyAnimated), [
          cond(and(not(clockRunning(clock)), not(animationState.finished)), [
            cond(
              isManuallyAnimated,
              set(config.toValue, animatedOverrideIndex),
              cond(
                or(shouldReset, not(shouldAnimate)),
                set(config.toValue, animatedStaticIndex),
                cond(
                  shouldAnimateNext,
                  set(config.toValue, add(animatedStaticIndex, 1)),
                  set(config.toValue, sub(animatedStaticIndex, 1))
                )
              )
            ),
            set(animationState.finished, 0),
            set(animationState.frameTime, 0),
            set(animationState.time, 0),
            startClock(clock),
          ]),
          timing(clock, animationState, config),
          cond(animationState.finished, finishTiming),
        ]),

        animationState.position,
      ]),
    [
      size,
      state,
      animatedOverrideIndex,
      animatedStaticIndex,
      animationState,
      clock,
      config,
      finishTiming,
      isManuallyAnimated,
      isTimingInterrupted,
      shouldAnimate,
      shouldAnimateNext,
      shouldReset,
      valueClamp,
    ]
  );
  return animatedPosition;
}
Example #4
Source File: index.tsx    From react-native-scroll-bottom-sheet with MIT License 4 votes vote down vote up
constructor(props: Props<T>) {
    super(props);
    const { initialSnapIndex, animationType } = props;

    const animationDriver = animationType === 'timing' ? 0 : 1;
    const animationDuration =
      (props.animationType === 'timing' && props.animationConfig?.duration) ||
      DEFAULT_ANIMATION_DURATION;

    const ScrollComponent = this.getScrollComponent();
    // @ts-ignore
    this.scrollComponent = Animated.createAnimatedComponent(ScrollComponent);

    const snapPoints = this.getNormalisedSnapPoints();
    const openPosition = snapPoints[0];
    const closedPosition = this.props.enableOverScroll
      ? windowHeight
      : snapPoints[snapPoints.length - 1];
    const initialSnap = snapPoints[initialSnapIndex];
    this.nextSnapIndex = new Value(initialSnapIndex);

    const initialDecelerationRate = Platform.select({
      android:
        props.initialSnapIndex === 0 ? ANDROID_NORMAL_DECELERATION_RATE : 0,
      ios: IOS_NORMAL_DECELERATION_RATE,
    });
    this.decelerationRate = new Value(initialDecelerationRate);

    const handleGestureState = new Value<GestureState>(-1);
    const handleOldGestureState = new Value<GestureState>(-1);
    const drawerGestureState = new Value<GestureState>(-1);
    const drawerOldGestureState = new Value<GestureState>(-1);

    const lastSnapInRange = new Value(1);
    this.prevTranslateYOffset = new Value(initialSnap);
    this.translationY = new Value(initialSnap);

    this.lastSnap = new Value(initialSnap);

    this.onHandleGestureEvent = event([
      {
        nativeEvent: {
          translationY: this.dragY,
          oldState: handleOldGestureState,
          state: handleGestureState,
          velocityY: this.velocityY,
        },
      },
    ]);
    this.onDrawerGestureEvent = event([
      {
        nativeEvent: {
          translationY: this.dragY,
          oldState: drawerOldGestureState,
          state: drawerGestureState,
          velocityY: this.velocityY,
        },
      },
    ]);
    this.onScrollBeginDrag = event([
      {
        nativeEvent: {
          contentOffset: { y: this.lastStartScrollY },
        },
      },
    ]);

    const didHandleGestureBegin = eq(handleGestureState, GestureState.ACTIVE);

    const isAnimationInterrupted = and(
      or(
        eq(handleGestureState, GestureState.BEGAN),
        eq(drawerGestureState, GestureState.BEGAN),
        and(
          eq(this.isAndroid, 0),
          eq(animationDriver, 1),
          or(
            eq(drawerGestureState, GestureState.ACTIVE),
            eq(handleGestureState, GestureState.ACTIVE)
          )
        )
      ),
      clockRunning(this.animationClock)
    );

    this.didGestureFinish = or(
      and(
        eq(handleOldGestureState, GestureState.ACTIVE),
        eq(handleGestureState, GestureState.END)
      ),
      and(
        eq(drawerOldGestureState, GestureState.ACTIVE),
        eq(drawerGestureState, GestureState.END)
      )
    );

    // Function that determines if the last snap point is in the range {snapPoints}
    // In the case of interruptions in the middle of an animation, we'll get
    // lastSnap values outside the range
    const isLastSnapPointInRange = (i: number = 0): Animated.Node<number> =>
      i === snapPoints.length
        ? lastSnapInRange
        : cond(
            eq(this.lastSnap, snapPoints[i]),
            [set(lastSnapInRange, 1)],
            isLastSnapPointInRange(i + 1)
          );

    const scrollY = [
      set(lastSnapInRange, 0),
      isLastSnapPointInRange(),
      cond(
        or(
          didHandleGestureBegin,
          and(
            this.isManuallySetValue,
            not(eq(this.manualYOffset, snapPoints[0]))
          )
        ),
        [set(this.dragWithHandle, 1), 0]
      ),
      cond(
        // This is to account for a continuous scroll on the drawer from a snap point
        // Different than top, bringing the drawer to the top position, so that if we
        // change scroll direction without releasing the gesture, it doesn't pull down the drawer again
        and(
          eq(this.dragWithHandle, 1),
          greaterThan(snapPoints[0], add(this.lastSnap, this.dragY)),
          and(not(eq(this.lastSnap, snapPoints[0])), lastSnapInRange)
        ),
        [
          set(this.lastSnap, snapPoints[0]),
          set(this.dragWithHandle, 0),
          this.lastStartScrollY,
        ],
        cond(eq(this.dragWithHandle, 1), 0, this.lastStartScrollY)
      ),
    ];

    this.didScrollUpAndPullDown = cond(
      and(
        greaterOrEq(this.dragY, this.lastStartScrollY),
        greaterThan(this.lastStartScrollY, 0)
      ),
      set(this.scrollUpAndPullDown, 1)
    );

    this.setTranslationY = cond(
      and(
        not(this.dragWithHandle),
        not(greaterOrEq(this.dragY, this.lastStartScrollY))
      ),
      set(this.translationY, sub(this.dragY, this.lastStartScrollY)),
      set(this.translationY, this.dragY)
    );

    this.extraOffset = cond(
      eq(this.scrollUpAndPullDown, 1),
      this.lastStartScrollY,
      0
    );
    const endOffsetY = add(
      this.lastSnap,
      this.translationY,
      multiply(1 - props.friction, this.velocityY)
    );

    this.calculateNextSnapPoint = (i = 0): Animated.Node<number> | number =>
      i === snapPoints.length
        ? this.tempDestSnapPoint
        : cond(
            greaterThan(
              abs(sub(this.tempDestSnapPoint, endOffsetY)),
              abs(sub(add(snapPoints[i], this.extraOffset), endOffsetY))
            ),
            [
              set(this.tempDestSnapPoint, add(snapPoints[i], this.extraOffset)),
              set(this.nextSnapIndex, i),
              this.calculateNextSnapPoint(i + 1),
            ],
            this.calculateNextSnapPoint(i + 1)
          );

    const runAnimation = ({
      clock,
      from,
      to,
      position,
      finished,
      velocity,
      frameTime,
    }: TimingParams) => {
      const state = {
        finished,
        velocity: new Value(0),
        position,
        time: new Value(0),
        frameTime,
      };

      const timingConfig = {
        duration: animationDuration,
        easing:
          (props.animationType === 'timing' && props.animationConfig?.easing) ||
          DEFAULT_EASING,
        toValue: new Value(0),
      };

      const springConfig = {
        ...DEFAULT_SPRING_PARAMS,
        ...((props.animationType === 'spring' && props.animationConfig) || {}),
        toValue: new Value(0),
      };

      return [
        cond(and(not(clockRunning(clock)), not(eq(finished, 1))), [
          // If the clock isn't running, we reset all the animation params and start the clock
          set(state.finished, 0),
          set(state.velocity, velocity),
          set(state.time, 0),
          set(state.position, from),
          set(state.frameTime, 0),
          set(timingConfig.toValue, to),
          set(springConfig.toValue, to),
          startClock(clock),
        ]),
        // We run the step here that is going to update position
        cond(
          eq(animationDriver, 0),
          timing(clock, state, timingConfig),
          spring(clock, state, springConfig)
        ),
        cond(
          state.finished,
          [
            call([this.nextSnapIndex], ([value]) => {
              if (value !== this.prevSnapIndex) {
                this.props.onSettle?.(value);
              }
              this.prevSnapIndex = value;
            }),
            // Resetting appropriate values
            set(drawerOldGestureState, GestureState.END),
            set(handleOldGestureState, GestureState.END),
            set(this.prevTranslateYOffset, state.position),
            cond(eq(this.scrollUpAndPullDown, 1), [
              set(
                this.prevTranslateYOffset,
                sub(this.prevTranslateYOffset, this.lastStartScrollY)
              ),
              set(this.lastStartScrollY, 0),
              set(this.scrollUpAndPullDown, 0),
            ]),
            cond(eq(this.destSnapPoint, snapPoints[0]), [
              set(this.dragWithHandle, 0),
            ]),
            set(this.isManuallySetValue, 0),
            set(this.manualYOffset, 0),
            stopClock(clock),
            this.prevTranslateYOffset,
          ],
          // We made the block return the updated position,
          state.position
        ),
      ];
    };

    const translateYOffset = cond(
      isAnimationInterrupted,
      [
        // set(prevTranslateYOffset, animationPosition) should only run if we are
        // interrupting an animation when the drawer is currently in a different
        // position than the top
        cond(
          or(
            this.dragWithHandle,
            greaterOrEq(abs(this.prevDragY), this.lastStartScrollY)
          ),
          set(this.prevTranslateYOffset, this.animationPosition)
        ),
        set(this.animationFinished, 1),
        set(this.translationY, 0),
        // Resetting appropriate values
        set(drawerOldGestureState, GestureState.END),
        set(handleOldGestureState, GestureState.END),
        // By forcing that frameTime exceeds duration, it has the effect of stopping the animation
        set(this.animationFrameTime, add(animationDuration, 1000)),
        set(this.velocityY, 0),
        stopClock(this.animationClock),
        this.prevTranslateYOffset,
      ],
      cond(
        or(
          this.didGestureFinish,
          this.isManuallySetValue,
          clockRunning(this.animationClock)
        ),
        [
          runAnimation({
            clock: this.animationClock,
            from: cond(
              this.isManuallySetValue,
              this.prevTranslateYOffset,
              add(this.prevTranslateYOffset, this.translationY)
            ),
            to: this.destSnapPoint,
            position: this.animationPosition,
            finished: this.animationFinished,
            frameTime: this.animationFrameTime,
            velocity: this.velocityY,
          }),
        ],
        [
          set(this.animationFrameTime, 0),
          set(this.animationFinished, 0),
          // @ts-ignore
          this.prevTranslateYOffset,
        ]
      )
    );

    this.translateY = interpolate(
      add(translateYOffset, this.dragY, multiply(scrollY, -1)),
      {
        inputRange: [openPosition, closedPosition],
        outputRange: [openPosition, closedPosition],
        extrapolate: Extrapolate.CLAMP,
      }
    );

    this.position = interpolate(this.translateY, {
      inputRange: [openPosition, snapPoints[snapPoints.length - 1]],
      outputRange: [1, 0],
      extrapolate: Extrapolate.CLAMP,
    });
  }