СМИ о нас
Прокачиваем анимацию
с react-native-reanimated.
Часть 1
#IT
Время чтения: 11 мин 14 сек
05 Августа 2021
#IT
Поделиться статьей
Подпишитесь на рассылку
Роман Турусов
Старший разработчик приложений IT-компании Lad
Сегодня хочу поговорить именно об анимации в приложениях, написанных с помощью react-native (далее RN), а точнее о библиотеке react-native-reanimated, заменяющая инструмент стандартного api Animated в RN.

В статье используется react-native-reanimated версии 1.13.3, поскольку начиная со второй версии библиотека получила много архитектурных обновлений. А также ограничения, которые противоречили важным фичам, использующимся в RN, и удобству отладки приложения.

Статья будет полезна тем, у кого есть бэкграунд в разработке мобильных приложений на RN.
Мотивация
Оптимизация

Первоначально проект был создан для решения проблемы взаимодействия с приёмником событий жестов — когда компонент можно перетаскивать по экрану. А при отпускании он привязывается к какому-либо месту. Несмотря на использование Animated.event и сравнивание текущего положения жеста с положением компонента на экране, а также выполнение всего этого взаимодействия в UI-потоке с флагом useNativeDriver, нам всё равно в конце анимации приходилось возвращать состояние жеста в JS. Это могло привести к потере кадров.

Это связано с тем, что выполнение анимации Animated.spring(props).start() не может использоваться по-настоящему декларативно. Когда функция вызывается, то возникает «побочный эффект» в виде запуска процесса (анимации). Он обновляет значение некоторое время.

Добавление узлов «побочных эффектов» в текущую реализацию Animated оказалось довольно сложной задачей — модель выполнения api запускает все зависимые узлы каждого рендера для компонентов, которые необходимо обновить. Разработчики библиотеки не хотели запускать «побочные эффекты» чаще, чем необходимо, потому что это, например, приведёт к многократному выполнению одной и той же анимации.

Удобство

Еще одним источником вдохновения для изменения внутреннего устройства Animated стала работа Krzysztof Magiera про перенос функциональности Анимированного отслеживания в собственный драйвер.





Стандартное Animated api оказалось поддерживает не всё, что могла делать неродная API. Одна из целей react-native-reanimated заключалась в предоставлении расширенной кодовой базы для создания API, которое позволяло писать более сложные анимации только на JS. И сделать код настолько минимальным, насколько это возможно.

Подход
В react-native-reanimated свойства анимации компонента объявляются в виде узлов (функций), которые передают поведение этого компонента в зависимости от значений, описанных в этих узлах. В сочетании с жестами можно запускать чисто нативные анимации, не пересекая мост между JS движком и нативной частью RN.

block([
cond(not(clockRunning(clock)), startClock(clock)),
timing(clock, state, config),
cond(state.finished,stopClock(clock))
])
К практике
Для начала будет полезным ознакомится с api Animated.
Установим

Shell

# Установим библиотеку
npm i react-native-reanimated@1.13.3

# Для iOS
cd ios
pod install

Также вам понадобится ndk версии 21.3.6528147 или выше.

Используем

Воспользуемся примером из документации и прокомментируем код. Он заключается в передвижении компонента по экрану. Для начала необходимо определить анимацию.
import Animated, {
  block,
  clockRunning,
  cond,
  Clock,
  debug,
  Easing,
  set,
  startClock,
  stopClock,
  timing,
  Value,
  useCode,
  Node,
  interpolateColors
} from 'react-native-reanimated';
 
const runTiming = (clock, value, dest) => {
 
  const state = {
    finished: new Value(0),
    position: new Value(0),
    time: new Value(0),
    frameTime: new Value(0),
  };
 
  const config = {
    duration: 5000,
    toValue: new Value(0),
    // Определяем какое будет "смягчение" относительно, линейного Clock 
    easing: Easing.inOut(Easing.ease),
  };
 
  // Возвращаем узел, который объединяет несколько функций, вызывает их
  // в порядке, в котором они передаются в block и возвращает результат 
  // последнего узла.
  return block([
    cond(
      clockRunning(clock),
      [
        // Если счетчик уже запущен, то мы обновляем значение toValue.
        set(config.toValue, dest),
      ],
      [
        // Если счётчик не запущен, то мы сбрасываем все значения и
    	  // запускаем счётчик.
        set(state.finished, 0),
        set(state.time, 0),
        set(state.position, value),
        set(state.frameTime, 0),
        set(config.toValue, dest),
        startClock(clock),
      ]
    ),
    // Мы определяем здесь шаг, который запускает процесс расчёта значений.
    timing(clock, state, config),
    // Если анимация закончилась, то мы останавливаем счётчик.
    cond(state.finished, debug('stop clock', stopClock(clock))),
    // Определяем узел возвращения обновлённого значения.
    state.position,
  ]);
}
Прежде чем верстать интерфейс, нужно сделать хук для создания ссылки на узел счётчика. Это расширенный объект анимированного значения Value. Он может обновляться в каждом рендере и возвращать его timestamp.

function useClock() {
const ref = useRef(new Clock());
return ref.current;
}
Затем сверстаем

export default () => {
  const clock = useClock();
 
  // Вызываем функцию runTiming, определённый выше, чтобы создать
  // узел, который будет использоваться для translateX трансформации. 
  const translateX = runTiming(clock, -120, 120)
 
  return (
     <View style={styles.container}>
       <Animated.View
         style={[styles.box, { transform: [{ translateX }] }]}
       />
     </View>
   );
}
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  box: {
    width: 50,
    height: 50,
    backgroundColor: '#4585E6'
  }
}


Сократим

Теперь реализуем универсальный инструмент, который может анимировать значения в зависимости от состояния. Для начала стоит разделить функцию расчета значений runTiming на функцию получения настроек для описания узлов анимации и хук, возвращающий анимированное значение для вёрстки.

interface AnimationTimingProps {
  value: Value;
  trigger: boolean;
  easing: Animated.EasingFunction;
  duration: number;
}
 
// Получить объект с настройками для описания узлов анимации
const getTimingSettings = ({
  value,
  trigger = false,
  easing = Easing.inOut(Easing.circle),
  duration = 300,
}: AnimationTimingProps) => {
  return {
    clock: new Clock(),
    state: {
      finished: new Value(0),
      position: value || new Value(trigger ? 1 : 0),
      time: new Value(0),
      frameTime: new Value(0),
    },
    config: {
      duration,
      toValue: new Value(trigger ? 0 : 1),
      easing,
    },
  };
};
 
// Определим какие настройки мы будем использовать для расчётов
interface HookTimingProps {
  trigger: boolean;
  range: [number, number];
  duration?: number;
  callback?: () => void;
  easing?: Animated.EasingFunction;
}
 
// Хук, возвращающий анимированное значение для вёрстки.
function useTiming({
  range: [from, to],
  callback,
  trigger = false,
  easing = Easing.inOut(Easing.circle),
  duration = 300,
}: HookTimingProps) {
  const value = useValue(trigger ? 1 : 0);
 
	// Хук запуска расчёта анимации
  useCode(() => {
    const { clock, config, state } = getTimingSettings({
      trigger,
      value,
      easing,
      duration,
    });
 
    return [
      cond(not(clockRunning(clock)), startClock(clock)),
      timing(clock, state, config),
      cond(state.finished, block([stopClock(clock), call([], callback)])),
      state.position,
    ];
  }, [trigger]);
 
	// мапим значения, потому что значение "value" менялось в интервале от 0 до 1
  return value.interpolate({
    inputRange: [0, 1],
    outputRange: [from, to],
    extrapolate: Extrapolate.CLAMP,
  });
}
Хук useCode в качестве первого параметра получает функцию фабрики, которая должна возвращать узел анимации или массив из узлов. Они будут затем переданы в узел block — и вторым параметром массив зависимостей. Функция обновляет коренной узел во время первого рендера и при каждом изменении значений в зависимостях.

Дальше понадобится изменить вёрстку.

const [trigger, setTrigger] = useState(false);
 
const translateX = useTiming({
trigger,
range: [-120, 120],
easing: Easing.inOut(Easing.cubic),
duration: 400,
// Вызов после выполнения анимации
callback: () => {
setTimeout(() => {
setTrigger(!trigger);
}, 600);
},
});




Теперь мы получили хук, который в зависимости от состояния возвращает новое анимированное значение, имеет настройки для изменения продолжительности анимации, «смягчение» вектора изменения компонента и возможность выполнить кастомную функцию после выполнения анимации.
Усложним

Также Reanimated умеет работать не только с числами, но и с цветом.

Мы можем усложнить функцию useTiming и получить не только числовое значение. На самом деле, различие функции расчёта анимации между числовым и цветовым значением различается только в итоговом преобразовании значения value в интервале числовых или цветовых from и to. Поэтому мы можем объединить всё в функции useTiming.

// ...
// Изменяем тип функции
function useTiming<P = number>({
  range: [from, to],
  callback = () => 0,
  trigger = false,
  easing = Easing.inOut(Easing.circle),
  duration = 300,
}: HookTimingProps): Animated.Node<P> {
// ...
 
// ...
// возвращаем из функции
if (typeof from === 'string' && typeof to === 'string') {
    // преобразовать число "value" в цвет, который будет находится в градиенте 
    // между from и to
    return interpolateColors(value, {
      inputRange: [0, 1],
      outputColorRange: [from, to],
	 }) as Animated.Node<P & string>;
 }
 return value.interpolate({
   inputRange: [0, 1],
   outputRange: [from, to],
   extrapolate: Extrapolate.CLAMP,
 }) as Animated.Node<P & number>;
// ...
Осталось лишь добавить узел в вёрстку

// Расположим рядом с translateX
const boxBackColor = useTiming({
  trigger,
  range: ['#4585E6', '#37BA96'],
});
// И добавим анимированный цвет к квадрату
<Animated.View
  style={[
    styles.box,
    { 
      transform: [{ translateX }],
      backgroundColor: boxBackColor,
    }, 
  ]} 
/>




Теперь useTiming может возвращать анимированое числовое и цветовое значение узла в зависимости от переданного интервала в range. Такого примера достаточно, чтобы создавать простые анимации и делать ваши приложения приятнее глазу.
Дальше что?
Сейчас мы рассмотрели пример базовой анимации, которая показывает основную возможность — создание её декларативно. react-native-reanimated имеет широкое api и множество возможностей для использования. Часть из них ещё необходимо будет разобрать. И мы вернёмся к этому в следующей части.
Другие статьи из раздела СМИ о нас
07 Октября
СМИ о нас  
Почему обучение технического писателя — это нетривиальная задача
01 Октября
СМИ о нас  
IT-компания Lad помогает бизнесу и государству управлять HR-капиталом
28 Сентября
СМИ о нас  
ООО «Платформа строительных сервисов» — серебряный партнер конференции #ГИСОГД2021
27 Сентября
СМИ о нас  
Проект управления HR капиталом региона представлен на «ПРОФ-IT»
27 Сентября
СМИ о нас  
Список региональных IT-проектов – победителей конкурса «ПРОФ-IT.2021»
27 Сентября
СМИ о нас  
IX Всероссийский форум «ПРОФ-IT» — фотолента
СМИ о нас  
ТВ: На старт благотворительного забега “Открытые сердца” вышли более 200 человек
30 Августа
СМИ о нас  
Топ-17 крупнейших ИT-компаний Нижнего Новгорода
18 Августа
СМИ о нас  
Нижегородская область лидирует по доступности мер поддержки IT-компаний
02 Августа
СМИ о нас  
Чем занимается технический писатель: опыт IT-компании Lad
30 Июля
СМИ о нас  
Пишем свой dependency free WebSocket сервер на Node.js
27 Июля
СМИ о нас  
Сколько стоит рекомендация IT-специалиста в Нижнем. Аналитика
16 Июля
СМИ о нас  
IT-компания Lad вошла в ТОП-5 работодателей по найму Junior-разработчиков
06 Июля
СМИ о нас  
Технический писатель: с чего начать
28 Июня
СМИ о нас  
10 эко-приложений, которые помогают спасать природу
27 Июня
СМИ о нас  
IT-кампус на 7 000 студентов создадут в Нижнем Новгороде
23 Июня
СМИ о нас  
В Нижнем Новгороде пройдет паблик-ток о развитии IT-сферы
23 Июня
СМИ о нас  
В Арсенале обсудят IT в Нижегородской области
16 Июня
СМИ о нас  
Обучающая программа «Веб-разработка» от HiBrain стартовала при ННГУ
15 Июня
СМИ о нас  
Оплатить ЖКХ в режиме онлайн теперь можно на портале «Карты жителя Нижегородской области»
10 Июня
СМИ о нас  
«Прекрасная идея» или напрасные затраты? IT-бизнес — о проекте редевелопмента Започаинья
10 Июня
СМИ о нас  
Нижегородские IT-компании смогут побороться за победу во всероссийском конкурсе «IT-проект: Back To The Product»
03 Июня
СМИ о нас  
Скидки и сертификаты стали доступны на «Карте жителя Нижегородской области»
01 Июня
СМИ о нас  
Фестиваль детских инновационных проектов прошел в технопарке «Кванториум Нижний Новгород»
26 Мая
СМИ о нас  
Нижегородские школьники разработали маску дополненной реальности для сервиса «Карта жителя Нижегородской области»
20 Мая
СМИ о нас  
Как интегратор 1С балуется плюшками
17 Мая
СМИ о нас  
Историческая викторина в честь 800-летия Нижнего Новгорода стартовала на “Карте жителя”
12 Мая
СМИ о нас  
Жители региона смогут получать цифровые квитанции через сервис «Карта жителя Нижегородской области
12 Мая
СМИ о нас  
Квитанции об оплате услуг ЖКХ можно получить на портале «Карта жителя»
27 Апреля
СМИ о нас  
Джентльменский набор приложений для смартфона в 2021 году
23 Апреля
СМИ о нас  
IT-компания Lad вошла в число крупнейших работодателей рынка IT в Нижнем Новгороде
16 Апреля
СМИ о нас  
Какой кошелек выбрать?
05 Апреля
СМИ о нас  
Формы платформы
04 Апреля
СМИ о нас  
На 30% Минстрой России прогнозирует повышение индекса IQ городов к 2024 году
01 Апреля
СМИ о нас  
К 2024 году в России планируется на 30% повысить индекс цифровизации городов
22 Марта
СМИ о нас  
Эко-квест «800 шагов к чистому городу»
18 Марта
СМИ о нас  
Все школы в России обеспечат доступом к интернету в этом году
06 Марта
СМИ о нас  
Команды из Нижегородской области стали победителями межрегионального онлайн-хакатона
06 Марта
СМИ о нас  
Кейс «Instagram-маски в честь 800-летия Нижнего Новгорода» от IT-компании Lad
01 Марта
СМИ о нас  
Где работать в ИТ в 2021: Lad
28 Февраля
СМИ о нас  
Нижегородский онлайн-хакатон «VRARHack52» соберет юных разработчиков со всей страны
22 Февраля
СМИ о нас  
Диагностируем проблемы в микросервисной архитектуре на Node.js с помощью OpenTracing и Jaeger
25 Декабря
СМИ о нас  
В российских школах заменяют Word и Excel на отечественные аналоги
24 Декабря
СМИ о нас  
Дмитрий Петров о налоговых льготах для IT-сферы
СМИ о нас  
Нижегородские IT-компании получат налоговые льготы
22 Декабря
СМИ о нас  
Профит для бизнеса от работы контактного центра на удаленке: кейс компании Lad
10 Декабря
СМИ о нас  
Росатом интегрирует решения российских разработчиков в проекты Smart City
07 Декабря
СМИ о нас  
Портал "Карта жителя Нижегородской области" стал удобнее
06 Декабря
СМИ о нас  
На портале «Карта жителя Нижегородской области» обновлен профиль пользователя
30 Ноября
СМИ о нас  
iCluster обсудит лучшие практики импортозамещения
27 Ноября
СМИ о нас  
Lad: цифровая трансформация бизнеса
19 Ноября
СМИ о нас  
IT-компания Lad поделится лучшими практиками
18 Ноября
СМИ о нас  
Компания Lad проведет день открытых дверей и расскажет о трендах IT-разработки
14 Ноября
СМИ о нас  
Участники регионального iCluster расскажут об отраслевых трендах
29 Октября
СМИ о нас  
О будущем проекта "Карта жителя Нижегородской области"
22 Октября
СМИ о нас  
Эксперт Lad дал комментарий Tadviser об импортозамещении офисного софта
14 Октября
СМИ о нас  
Как ИТ-специалисту устроиться на работу в хорошую компанию
07 Октября
СМИ о нас  
Карту жителя Нижегородской области начнут тестировать в октябре
06 Октября
СМИ о нас  
К сервису «Карта жителя Нижегородской области» присоединились первые банки-партнеры
01 Октября
СМИ о нас  
Пакет офисных решений "Р7-Офис" поступит в школы Ингушетии
01 Октября
СМИ о нас  
Ингушетия получит 3 тыс. лицензий на российское ПО для системы образования
25 Августа
СМИ о нас  
НБД-Банк и IT-компания Lad предлагают малому бизнесу онлайн-кассы в аренду
18 Июня
СМИ о нас  
Алтайский край переходит на российское ПО
06 Мая
СМИ о нас  
Work must go on!
03 Апреля
СМИ о нас  
IT-компании предлагают свои продукты бесплатно
26 Марта
СМИ о нас  
ПСС на Digital.Forum Construction 2020
24 Марта
СМИ о нас  
Быстрая цифровизация строительной отрасли
16 Марта
СМИ о нас  
Партнерская акция IT-компании Lad и НБД-Банка
13 Февраля
СМИ о нас  
Отечественное ПО для белгородских школьников
СМИ о нас  
Платформа HiBrain - разработка IT-компании Lad
СМИ о нас  
Digital Summit 2019
24 Октября
СМИ о нас  
В Нижнем Новгороде iCluster провёл IT-фестиваль "iFest 2019"
21 Октября
СМИ о нас  
Lad на фестивале программистов IFest
03 Октября
СМИ о нас  
Обрабатываем заказы из интернет магазина с помощью RabbitMQ и TypeScript
27 Сентября
СМИ о нас  
Lad примет участие в Digital Summit 2019
СМИ о нас  
Как IT-технологии изменили жизнь нижегородцев?
06 Августа
СМИ о нас  
В Нижегородской области запустят ПСС
07 Июня
СМИ о нас  
На ПМЭФ презентованы проекты IT-компании Lad
06 Июня
СМИ о нас  
На ПМЭФ презентованы ключевые нижегородские ИТ проекты
29 Мая
СМИ о нас  
В Нижнем Новгороде запустили цифровую платформу IT-образования HiBrain
24 Апреля
СМИ о нас  
Kari втрое ускорила оформление рассрочки
12 Февраля
СМИ о нас  
Цифровизация Retail-отрасли: актуальные тренды
23 Ноября
СМИ о нас  
Лучшие IT-проекты выбрали в Нижнем Новгороде
Поделитесь вашим мнением
Ответ успешно отправлен Мы свяжемся с вами в ближайшее время

Файлы cookies

Мы используем файлы cookies. Это необходимо для анализа трафика и корректной работы сайта. Продолжая работу с сайтом, вы подтверждаете свое согласие на применение этих технологий.

подробнее хорошо