Анімації в мобільних додатках: Lottie, Rive, Spring та Reanimated
Анімація либо працює при 60/120 fps і відчувається природно, либо видна в Xcode Instruments як червоні смуги на головному потоці. Проміжного варіанту немає.
Чому UIView.animate Ламається на Складних Сценаріях
UIView.animate(withDuration:) та ObjectAnimator на Android — правильний вибір для простих переходів. Але як тільки анімація стає інтерактивною (користувач тягне елемент, швидкість залежить від жесту), потрібен інший підхід.
На iOS для gesture-driven анімації правильний інструмент — UIViewPropertyAnimator. Він дозволяє зупиняти, обертати та змінювати анімацію в процесі. Типовий випадок: bottom sheet, який слідує за пальцем, продовжує рух з інерцією після відпускання та притягується до найближчої позиції. З UIView.animate це либо не працює зовсім, либо потребує ручної фізики.
У SwiftUI withAnimation працює з коробки, але інтерактивність обмежена — немає прямого аналога UIViewPropertyAnimator. Обхідний шлях: .gesture(DragGesture()) + @GestureState + явне обчислення позиції. Або використовуємо SwiftUI Animations API з Animation.spring(duration:bounce:) з iOS 17.
React Native Reanimated: Worklets на UI Thread
React Native Animated API виконує анімації в JS thread — це джерело джанку при навантаженому bridge. Reanimated 3 вирішує проблему через worklets: функції, які компілюються та виконуються прямо на UI thread без переходу JS-моста.
Приклад: parallax scroll header. На базовому Animated.Value при швидкому скролінгу FPS падає до 40-45 на mid-range Android. На Reanimated з useAnimatedScrollHandler — стабільні 60 fps, тому що весь пересчіт позиції відбувається на UI thread.
Reanimated 3 з useSharedValue, useAnimatedStyle та withSpring/withTiming — це поточний стандарт для анімацій у React Native. Gesture Handler v2 щільно інтегрований: useAnimatedGestureHandler замінює PanResponder та теж працює на UI thread.
Lottie vs Rive
Обидва інструменти вирішують задачу «дизайнер робить анімацію, розробник додає файл». Але принципово по-різному.
Lottie експортує After Effects анімацію в JSON. Рендеринг — векторний, підтримує iOS (lottie-ios), Android (lottie-android), Flutter (lottie), React Native (lottie-react-native). Обмеження: немає інтерактивності (анімація лінійна), великі JSON-файли з частинками та blur-ефектами рендеряться важко. Blur через Lottie на Android — гарантований FPS drop.
Rive — state machine. Анімація має стани та переходи між ними, керовані з коду через StateMachineInput. Кнопка з hover, pressed, loading, success станами — це одна Rive-анімація з чотирма станами, а не чотири окремих файли. Рендеринг апаратний через Metal/OpenGL. Розмір файлів менший за Lottie через бінарний формат .riv.
Вибір простий: статична декоративна анімація (splash screen, onboarding ілюстрації) — Lottie. Інтерактивні UI-елементи з станами — Rive.
Spring-фізика та Hero Transitions
Spring-анімація відчувається природно тому що імітує фізику — масу, жорсткість та демпфування. У SwiftUI: Animation.spring(response:dampingFraction:). У Android Compose: spring(dampingRatio = Spring.DampingRatioMediumBouncy).
Для Hero-переходів (елемент «перелітає» між екранами) на iOS використовуємо UIViewControllerTransitioningDelegate + UIViewControllerAnimatedTransitioning. У SwiftUI з iOS 17 — matchedTransitionSource + navigationTransition(.zoom). На Flutter — Hero віджет, який працює з коробки.
Типова помилка в Hero transitions: анімація починається нормально, але на цільовому екрані елемент «стрибає» у фінальну позицію. Причина — AutoLayout constraints застосовуються до завершення анімації. Рішення: layoutIfNeeded() в блоці анімації або використання transform замість frame-змін.
Терміни на анімаційний шар: базові екранні переходи та мікроінтеракції — 1 тиждень. Lottie/Rive інтеграція з design system — 3-5 днів після отримання фінальних файлів. Кастомна gesture-driven інтерактивність (sheet, drawer, карусель з фізикою) — 1-2 тижні.







