Создание непрерывных анимаций в React Native с Animated.loop()

Зачем нужен Animated.loop
Animated.loop() — это удобный способ сделать анимацию цикличной. Основная идея: создаётся Animated.Value, который меняет своё значение на каждом кадре анимации, а Animated.loop() повторяет указанную анимацию столько раз, сколько нужно (или бесконечно по умолчанию). Это полезно для индикаторов загрузки, вращающихся иконок, циклических переходов и фоновых эффектов.
Термин в одну строку:
- Animated.Value — числовой контейнер, который обновляется анимацией.
- Animated.timing — тайминговая анимация, изменяет значение с течением времени.
- useNativeDriver — флаг, который выполняет анимацию на нативной стороне для лучшей производительности (не поддерживает все свойства).
Базовое использование Animated.loop
Шаги:
- Создайте Animated.Value.
- Опишите анимацию (например, Animated.timing).
- Оберните анимацию в Animated.loop({ iterations: N }).
- Запустите .start().
Ниже исправленный и читабельный пример кода, который делает непрерывную вращающуюся анимацию логотипа React:
import React, { useEffect, useRef } from 'react';
import { StyleSheet, View, Animated, Image } from 'react-native';
export default function App() {
const spinValue = useRef(new Animated.Value(0)).current;
useEffect(() => {
const spin = spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const timing = Animated.timing(spinValue, {
toValue: 1,
duration: 2000, // 2000 ms = 2 с
useNativeDriver: true,
isInteraction: false,
});
Animated.loop(timing).start();
// Опционально: очистка при размонтировании
return () => spinValue.stopAnimation();
}, [spinValue]);
const spin = spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
return (
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});Важно: в transform используйте интерполированное значение (spin), а не сам Animated.Value напрямую. В примере duration указано как 2000 ms (2 с).
Управление количеством повторов и остановка
- По умолчанию Animated.loop() создаёт бесконечный цикл.
- Чтобы задать фиксированное число повторов, передайте параметр iterations: Animated.loop(timing, { iterations: 5 }).
- Для остановки — сохраните ссылку на анимацию и вызовите .stop(), либо используйте условие в useEffect, которое снимает слушатели и вызывает stopAnimation на Animated.Value.
Пример с ограниченным числом итераций:
Animated.loop(timing, { iterations: 5 }).start(() => {
// колбэк после завершения всех итераций
});Работа с комплексными анимациями
Сложные сценки обычно состоят из нескольких независимых анимаций (положение, вращение, прозрачность). Рекомендации:
- Разбивайте анимацию на этапы
- Легче отлаживать и комбинировать.
- Можно параллельно или последовательно объединять их в Animated.parallel или Animated.sequence.
- Используйте Animated.sequence для последовательности
- Animated.sequence([anim1, anim2, anim3]) выполняет набор анимаций одна за другой.
- Оберните sequence в Animated.loop, чтобы повторять всю последовательность.
Пример: показать → повернуть → скрыть (зациклено)
const sequence = Animated.sequence([
Animated.timing(opacityValue, { toValue: 1, duration: 400, useNativeDriver: true }),
Animated.timing(spinValue, { toValue: 1, duration: 1200, useNativeDriver: true }),
Animated.timing(opacityValue, { toValue: 0, duration: 400, useNativeDriver: true }),
]);
Animated.loop(sequence).start();Когда Animated.loop может не подойти
- Нужна точная физическая симуляция (меньше контроля над фреймами): лучше Reanimated 2 или физические движки.
- Нужно анимировать layout-свойства (width/height/margin) с useNativeDriver — это не поддерживается. Для таких свойств используйте LayoutAnimation или анимируйте трансформации вместо размеров.
- Сложные последовательности с условной логикой — удобнее реализовать с управлением состояния и явными таймерами.
Альтернативные подходы
- Reanimated 2/3: больше возможностей, работа на нативной стороне и поддержка сложных вычислений без мостов.
- Lottie: для векторных сложных анимаций, импортированных из Adobe After Effects.
- react-native-animatable: быстрые примитивы для простых UI-анимаций.
Производительность и useNativeDriver
useNativeDriver: true переносит анимацию в нативную очередь рендеринга, что снижает задержки и джиттер. Однако нативный драйвер не поддерживает все CSS-подобные свойства (например, width или backgroundColor не всегда поддерживаются). Правило:
- Анимируйте transform и opacity через useNativeDriver.
- Для остальных свойств ищите обходы (scale вместо width, маскировка) или используйте JS-driven анимации для свойств, которые нужно менять через JS.
Important: если вы видите «Property XYZ is not supported by native animated module», замените свойство или снимите useNativeDriver и измерьте влияние на производительность.
Проверка и критерии приёмки
Критерии приёмки:
- Анимация запускается и визуально плавная на тестовых устройствах (iOS и Android).
- При заданных iterations анимация остановлена после нужного числа циклов.
- При размонтировании компонента анимация не вызывает утечек памяти (stopAnimation вызывается).
- Использование useNativeDriver не ломает желаемые свойства.
Тесты/acceptance:
- Функциональный: логотип вращается без рывков при 60 FPS (проверка на реальном устройстве).
- Стабильность: старт/стоп анимации 50 раз подряд без падений.
- Ресурсы: мониторинг CPU/GPU при работе анимации, отсутствие значительного перегрева на старых устройствах.
Роль‑ориентированные чек-листы
Разработчик:
- Разбил сложную анимацию на мелкие шаги.
- Проверил useNativeDriver и поддержку свойств.
- Добавил очистку анимации в unmount.
Дизайнер:
- Предоставил референс анимации (видео или прототип).
- Уточнил длительности, easing и поведение в разных состояниях.
QA:
- Протестировал на минимально поддерживаемых устройствах.
- Проверил итерации, остановку и переходы между состояниями.
Модель принятия решений (набросок)
- Нужна ли нативная производительность? -> Да: useNativeDriver / Reanimated.
- Требуется ли анимация layout? -> Да: LayoutAnimation или JS-driven.
- Сложная последовательность/физика? -> Reanimated или физические движки.
Советы по отладке
- Логи: spinValue.addListener(value => console.log(value));
- Проверяйте, что interpolate возвращает строку с ‘deg’ для rotate.
- Убедитесь, что Animated.Value сбрасывается/реинициализируется при необходимости.
Факт-бокс — ключевые числа
- duration: часто 2000 ms (2 с) для одного оборота.
- iterations: по умолчанию бесконечно (omit для бесконечного цикла).
- useNativeDriver: рекомендуется для transform и opacity.
Совместимость и миграция
- Animated API стабилен в React Native, но для высоконагруженных и сложных анимаций рассмотрите Reanimated v2/v3.
- При миграции с Animated на Reanimated потребуется переписать логику анимаций и интерполяций на хуки Reanimated.
Короткие рекомендации по безопасности и приватности
Анимации не отправляют данные пользователей и не влияют на приватность напрямую. Обратите внимание на вычисления в реальном времени: избегайте логирования чувствительных данных внутри listeners.
Заключение
Animated.loop — простой и эффективный инструмент для создания зацикленных анимаций в React Native. Разбивайте сложные эффекты на части, используйте Animated.sequence/parallel, и подключайте useNativeDriver для оптимальной производительности. Для очень сложных и производительных задач рассмотрите Reanimated.
Ключевые выводы:
- Animated.loop удобно зацикливать простые и составные анимации.
- Используйте интерполяцию при работе с rotate и другими преобразованиями.
- Разбивайте сложные сценарии и тестируйте на реальных устройствах.
Похожие материалы
Как смотреть видео в VR: Oculus, Rift, Vive, Gear, Daydream
Как сохранить изображения из Google Images
Фокус-стекинг вручную в Photoshop
Как исправить синхронизацию iPod с компьютером
Длительная выдержка на Samsung Galaxy