Only run Reanimated animation on TextInput focus/blur

1.2k views Asked by At

I am currently learning to create React Native Animations with the RN Reanimated and Redash libraries. I've managed to create a simple timing animation which transitions a TextInput placeholder into a label.

  import Animated, { Clock, Easing, set, useCode } from 'react-native-reanimated';
  import { timing } from 'react-native-redash/lib/module/v1';

  const [isFocused, setIsFocused] = useState(false);
  const clock = new Clock();
  const [animation] = useState(new Animated.Value(0));
  const shouldAnimateLabel = isFocused || !!value;

  useCode(() =>
    set(
      animation,
      timing({
        clock,
        animation,
        duration: 150,
        from: shouldAnimateLabel ? 0 : 1,
        to: shouldAnimateLabel ? 1 : 0,
        easing: Easing.inOut(Easing.ease),
      }),
      [shouldAnimateLabel],
    ),
  );

  const animatedStyles = {
    top: Animated.interpolate(animation, {
      inputRange: [0, 1],
      outputRange: [20, -5],
    }),
    fontSize: Animated.interpolate(animation, {
      inputRange: [0, 1],
      outputRange: [18, 14],
    }),
    color: Animated.interpolateColors(animation, {
      inputRange: [0, 1],
      outputColorRange: ['#aaa', '#fff'],
    }),
  };

This animation is working fine when focusing/blurring the input, but as useCode runs on mount, I am currently getting the unwanted side effect of the label animating from 1 to 0 before I have interacted with either of the inputs.

enter image description here

Is there a common solution to this using react-native-reanimated or react-native-redash? I could add another isMounted state or something but that seems like a clunky solution?

1

There are 1 answers

1
Iosif On

Maybe something like this:

import Animated, { Clock, Easing, set, useCode } from 'react-native-reanimated';
import { timing } from 'react-native-redash/lib/module/v1';

const [isFocused, setIsFocused] = useState(null);
const clock = new Clock();
const [animation] = useState(new Animated.Value(0));
const shouldAnimateLabel = isFocused === null ? null : isFocused || !!value;

useCode(() =>
  set(
    animation,
    timing({
      clock,
      animation,
      duration: 150,
      from: shouldAnimateLabel ? 0 : 1,
      to: shouldAnimateLabel || shouldAnimateLabel === null ? 1 : 0,
      easing: Easing.inOut(Easing.ease),
    }),
    [shouldAnimateLabel],
  ),
);

const animatedStyles = {
  top: Animated.interpolate(animation, {
    inputRange: [0, 1],
    outputRange: [20, -5],
  }),
  fontSize: Animated.interpolate(animation, {
    inputRange: [0, 1],
    outputRange: [18, 14],
  }),
  color: Animated.interpolateColors(animation, {
    inputRange: [0, 1],
    outputColorRange: ['#aaa', '#fff'],
  }),
};