Основы анимации
Анимация — плавное изменение стиля во времени: прозрачности, положения, размера. На телефоне она подсказывает, что произошло: новая задача плавно появилась, удалённая — исчезла. В React Native есть встроенный модуль Animated, а для сложных случаев — библиотека Reanimated.
Animated: плавное появление
Сделаем обёртку, которая проявляет содержимое за 400 миллисекунд:
// components/FadeIn.tsx
import { type ReactNode, useEffect, useRef } from 'react';
import { Animated } from 'react-native';
export default function FadeIn({ children }: { children: ReactNode }) {
const opacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 400,
useNativeDriver: true,
}).start();
}, [opacity]);
return <Animated.View style={{ opacity }}>{children}</Animated.View>;
}Как это работает:
Animated.Value(0)— анимируемое число, начальная прозрачность 0.useRefсохраняет его между рендерами.Animated.View—View, который понимает такие значения вstyle.Animated.timingменяет значение доtoValueзаdurationмиллисекунд,start()запускает анимацию.useNativeDriver: trueотдаёт анимацию родному коду, и она остаётся плавной, даже если JavaScript занят.
Использование
Оберните строку задачи в renderItem списка:
// app/index.tsx
import FadeIn from '@/components/FadeIn';
renderItem={({ item }) => (
<FadeIn>
<TaskItem task={item} onToggle={toggleTask} />
</FadeIn>
)}Добавьте задачу — она не появится резко, а плавно проявится. Задачи, которые уже были на экране, не мигают: анимация срабатывает только при первом показе компонента.
Что можно анимировать с useNativeDriver
| Можно | Нельзя |
|---|---|
opacity | width, height |
transform: сдвиг, масштаб, поворот | backgroundColor |
margin, padding |
Для свойств раскладки useNativeDriver ставят false, но такая анимация менее плавная.
Reanimated
Библиотека react-native-reanimated уже установлена в шаблоне Expo. Она выполняет анимацию целиком в родном потоке и даёт готовые анимации появления и исчезновения:
// components/TaskItem.tsx
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
<Animated.View entering={FadeIn} exiting={FadeOut}>
<Text>{task.title}</Text>
</Animated.View>С entering и exiting задача плавно появляется при добавлении и плавно исчезает при удалении — без ручных значений и эффектов. Для анимаций, управляемых вручную, у Reanimated есть хуки useSharedValue и useAnimatedStyle и функция withTiming.
Частая ошибка
Создавать Animated.Value прямо в теле компонента: const opacity = new Animated.Value(0);. При каждом рендере появляется новое значение, равное нулю, и анимация сбрасывается или не идёт вовсе. Храните его в useRef, как в примере.