Урок по треку React Native 7 мин

Анимации и жесты: Animated, Reanimated, Gesture Handler

Animated и native driver, Reanimated: shared values и worklets, Gesture Handler.

Плавная анимация это 60 кадров в секунду, а на экранах 120 Гц ещё быстрее. Главный вопрос анимаций в RN: на каком потоке считается каждый кадр. Если на JS-потоке, любая работа React (рендер списка, разбор JSON) даст рывок.

Animated

Встроенный API:

const opacity = useRef(new Animated.Value(0)).current;

useEffect(() => {
  Animated.timing(opacity, {
    toValue: 1,
    duration: 300,
    useNativeDriver: true,
  }).start();
}, []);

<Animated.View style={{ opacity }} />
  • Animated.timing, spring, decay; композиция sequence, parallel, stagger, loop.
  • interpolate переводит значение в другой диапазон: opacity.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '90deg'] }).

useNativeDriver: true отправляет описание анимации на нативную сторону один раз, и дальше кадры считаются без JS. Ограничение: только свойства, не влияющие на раскладку, то есть transform и opacity. width, height, top, цвета фона нативный драйвер не поддерживает, такие анимации идут через JS.

Reanimated

Стандарт для сложных анимаций и жестов. Ключевые понятия:

  • Shared value (useSharedValue): значение, доступное и JS, и UI-потоку. Меняется через .value.
  • Worklet: маленькая функция, которая выполняется на UI-потоке. Колбэки useAnimatedStyle, обработчики жестов и withTiming работают как worklets.
  • useAnimatedStyle: стиль, пересчитываемый на UI-потоке при изменении shared value, без перерисовки компонента React.
const offset = useSharedValue(0);
const style = useAnimatedStyle(() => ({
  transform: [{ translateX: offset.value }],
}));
const onPress = () => {
  offset.value = withSpring(offset.value + 100);
};
  • Из worklet вызвать обычную JS-функцию напрямую нельзя: нужен runOnJS(fn)(args) (или его аналоги в новых версиях).
  • Изменение shared value не вызывает рендер React. Если значение нужно показать в обычном Text, его переносят в состояние через useAnimatedReaction и runOnJS.
  • Анимировать можно и свойства раскладки (width, height), так как вычисление идёт на UI-потоке.
  • Layout animations (entering, exiting, layout) анимируют появление, удаление и перестановку элементов.

Reanimated 4 требует новую архитектуру, вынес worklets в отдельный пакет react-native-worklets и добавил анимации в стиле CSS (переходы и ключевые кадры прямо в стилях).

Жесты

Встроенная система ответчиков (PanResponder) работает на JS-потоке. react-native-gesture-handler распознаёт жесты нативно и вместе с Reanimated даёт жесты, которые следуют за пальцем без участия JS:

const x = useSharedValue(0);
const pan = Gesture.Pan()
  .onChange((e) => { x.value += e.changeX; })
  .onEnd(() => { x.value = withSpring(0); });
const style = useAnimatedStyle(() => ({ transform: [{ translateX: x.value }] }));

<GestureDetector gesture={pan}>
  <Animated.View style={style} />
</GestureDetector>

Жесты комбинируются: Gesture.Simultaneous (одновременно, например масштаб и поворот), Gesture.Exclusive (один из, например двойной тап важнее одиночного), Gesture.Race. Корень приложения оборачивают в GestureHandlerRootView.

Как выбрать

ЗадачаИнструмент
Появление, прозрачность, сдвигAnimated с native driver или Reanimated
Анимация, связанная с жестомGesture Handler + Reanimated
Анимация раскладки спискаReanimated layout animations, LayoutAnimation
Сложная иллюстрация от дизайнераLottie, Rive

Частые ошибки

  • Анимировать width с useNativeDriver: true: ошибка.
  • setState на каждом кадре анимации.
  • Читать sharedValue.value в рендере и ждать перерисовки.
  • Вызывать JS-функцию из worklet без runOnJS.

Что спросят на собесе

  • Что такое native driver и его ограничения.
  • Как устроен Reanimated: shared values, worklets, UI-поток.
  • Почему анимация на JS-потоке дёргается при рендере.
  • Как сделать перетаскивание, которое не тормозит.
  • Как скомбинировать жесты.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.