Оптимізація анімацій для досягнення 60 FPS у мобільному додатку

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1735 послуг
Оптимізація анімацій для досягнення 60 FPS у мобільному додатку
Складний
~2-3 дні
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Оптимізація анімацій для досягнення 60 FPS у мобільній програмі

60 FPS — це 16.67 мс на кадр. Якщо хоч один кадр займе 17+ мс, Instruments показує dropped frame. На 120 Гц-дисплеях (ProMotion) поріг ще жорсткіший: 8.3 мс. Користувачі iPhone 13 Pro та Pixel 8 це фізично відчувають.

Де теряються кадри: діагностика спочатку

Перше, ніж що-то оптимізувати — відкрийте Xcode Instruments з шаблоном Core Animation. Запустіть на реальному пристрої (симулятор не рахується: інший GPU). Дивіться на два графіки: FPS та CPU Usage. Червоні стовпці на FPS-графіку — dropped frames.

На Android — Android Profiler у режимі GPU/CPU + Systrace для детального трейса. В Developer Options включаємо Profile GPU Rendering (відображає стовпці на екрані): якщо оранжева зона (Draw) та червона (Sync/Upload) регулярно перевищують 16ms-лінію — є проблема.

Типова знахідка: анімація тені (shadowRadius, shadowOffset на CALayer) перераховує Gaussian blur на CPU кожен кадр. На iPhone SE 2nd gen це може давати 8–10 мс тільки на тінь.

Головний принцип: GPU-шари проти CPU-рендеру

Анімуйте лише transform та opacity — не рекомендація, це закон продуктивності. Ці властивості обробляються Compositor thread напрямки, без участі main thread та без виклику drawRect:.

Все інше запускає Layout → Display → Prepare → Commit цикл:

Властивість Де малюється Dropped frames
transform, opacity GPU Compositor Ні
backgroundColor GPU (CALayer) Рідко
bounds, frame CPU → GPU Часто
cornerRadius + masksToBounds CPU (offscreen) Часто
shadowPath (статична) GPU Ні
shadowRadius (динамічна) CPU Дуже часто

cornerRadius з masksToBounds = true — offscreen rendering. Для кожного такого шару Core Animation робить додатковий render pass. В Instruments: Debug → Color Offscreen-Rendered фарбує їх жовтим. Виправлення: встановити layer.shadowPath статично або використовувати маску з векторного зображення.

UIKit: конкретні прийоми

shouldRasterize — обережно

layer.shouldRasterize = true кешує шар як bitmap. Допомагає якщо контент не змінюється. Вбиває, якщо змінюється: кеш інвалідується кожен кадр та перерисовується дорожче, ніж без нього. Перевіряємо через Instruments → Color Hits Green and Misses Red: червоне = інвалідація, допомоги нема.

drawRect vs CALayer

Переопределення drawRect: — ядерний варіант. Якщо виклик відбувається під час анімації (наприклад, змінюється bounds), main thread зайнятий рисуванням. Альтернатива: виносити статичний контент до окремого CALayer з contents = image.cgImage, анімувати лише transform.

CADisplayLink для користувацьких анімацій

Якщо пишемо користувацьку анімацію на CADisplayLink — прив'язуємося до preferredFramesPerSecond:

let displayLink = CADisplayLink(target: self, selector: #selector(tick))
displayLink.preferredFrameRateRange = CAFrameRateRange(
    minimum: 60,
    maximum: 120,
    preferred: 120
)
displayLink.add(to: .main, forMode: .common)

На ProMotion пристроях це дозволяє анімаціям працювати на 120 FPS. Без вказання range система може зафіксувати 60 навіть на 120 Гц дисплеї.

Lottie: часті проблеми з продуктивністю

Lottie за умовчанням використовує .automatic render mode. На складних анімаціях з масками та trim paths це часто означає CPU-рендер. Примусово переключаємо:

animationView.renderingEngine = .coreAnimation

Core Animation engine (.coreAnimation) рендерить через CALayers — без участі main thread. Обмеження: не підтримує деякі складні ефекти (gradients через trim paths, деякі blending modes). Перевіряємо в Lottie Diagnostics.

Compose: рекомендації

Modifier.graphicsLayer замість прямого змінення layout-параметрів:

// Погано: викликає relayout на кожен кадр
Box(modifier = Modifier.size(animatedSize))

// Добре: лише GPU transform, layout стабільна
Box(modifier = Modifier
    .size(100.dp)
    .graphicsLayer { scaleX = animatedScale; scaleY = animatedScale }
)

graphicsLayer працює аналогічно layer.transform у UIKit — поза layout pass.

Уникайте remember { mutableStateOf() } всередині анімаційної лямбди. Кожне оновлення стану через mutableStateOf викликає recomposition. Використовуйте Animatable напрямки, або animateFloatAsState який оновлює лише graphicsLayer без recompose екрана.

Типові випадки оптимізації

В програмі одного клієнта список з користувацькими ячейками падав до 40 FPS при скролі. Причина: кожна ячейка мала layer.cornerRadius = 12 з masksToBounds = true та layer.shadowRadius = 8. Подвійний offscreen render pass на кожну ячейку. Рішення: corner radius через UIBezierPath маску (один GPU pass), тінь через shadowPath з заздалегідь розрахованим CGPath. FPS повернувся на 60 стабільно.

Процес оптимізації

Профілювання в Instruments / Android Profiler на реальних пристроях (мінімум один повільний девайс з цільової аудиторії). Ідентифікація offscreen rendering, expensive draw calls, CPU-анімацій. Послідовне усунення з вимірюванням після кожної зміни. Регресійне тестування на пристроях різних класів.

Орієнтири по часу

Аудит та виправлення анімацій у існуючій програмі — 2–5 днів залежно від масштабу та числа проблемних місць.