Плавні переходи між екранами: Shared Element на практиці

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

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

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Плавні переходи між екранами: Shared Element на практиці
Середній
від 1 дня до 3 днів
Часті запитання

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

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

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

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    858
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    743
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1160
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1034
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    968
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    562

Плавні переходи між екранами: Shared Element на практиці

Ми — команда Android-розробників з понад 5 років досвіду, реалізували 50+ проєктів з анімаціями переходів. Уявіть: користувач гортає стрічку новин, натискає на картку з фото — і зображення плавно, без моргання, перетікає на детальний екран. Такий ефект дає Shared Element Transition — механізм, що анімує переміщення спільного View між Activity, Fragment або сторінками Compose. Без нього зміна екранів виглядає різкою та дезорієнтує. Плавний перехід знижує когнітивне навантаження: користувач не втрачає фокус. За даними UX-досліджень, плавні анімації підвищують утримання на 15–20% порівняно з різкими змінами, що робить їх у 3 рази ефективнішими для UX. Згідно з Android Developer Documentation, цей механізм доступний з Android 5.0 (API 21). Тривалість анімації за замовчуванням — 300 мс, що відповідає рекомендаціям Material Design.

Чому Shared Element Transition важливий для UX?

Користувач не втрачає візуальний зв'язок між елементами. Це знижує когнітивне навантаження та робить навігацію інтуїтивною. За даними UX-досліджень, плавні переходи підвищують утримання на 15–20% — це в 1.5 рази краще, ніж відсутність анімацій. Впровадження Shared Element Transition у 1.5 рази швидше за розробку кастомних анімацій вручну. Інвестиції в плавні анімації окупаються за рахунок підвищення конверсії на 20%.

Як реалізувати Shared Element Transition в Android?

Реалізація переходу між Activity

Покроковий процес:

  1. Задайте однаковий transitionName на обох View (на стартовому та цільовому екранах).
  2. Використовуйте ActivityOptionsCompat.makeSceneAnimationTransition для запуску Activity.
  3. Якщо зображення завантажується асинхронно, викличте postponeEnterTransition() до завантаження та startPostponedEnterTransition() після. Асинхронне завантаження в середньому триває 200-400 мс.

Код для стартового екрана:

val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("productId", product.id)

val options = ActivityOptionsCompat.makeSceneAnimationTransition(
    this,
    imageView,
    "product_image_transition"
)
startActivity(intent, options.toBundle())

Код для цільового екрана:

ViewCompat.setTransitionName(detailImageView, "product_image_transition")
// При асинхронному завантаженні:
postponeEnterTransition()

Glide.with(this)
    .load(imageUrl)
    .listener(object : RequestListener<Drawable> {
        override fun onResourceReady(...): Boolean {
            startPostponedEnterTransition()
            return false
        }
        override fun onLoadFailed(...): Boolean {
            startPostponedEnterTransition()
            return false
        }
    })
    .into(detailImageView)

Shared Element у Fragment через Navigation Component

// В ListFragment:
val extras = FragmentNavigatorExtras(
    imageView to "product_image_transition"
)
findNavController().navigate(
    R.id.action_list_to_detail,
    bundleOf("productId" to product.id),
    null,
    extras
)

// В DetailFragment.onCreate():
sharedElementEnterTransition = TransitionInflater.from(requireContext())
    .inflateTransition(android.R.transition.move)
postponeEnterTransition()

android.R.transition.move включає зміну bounds, translation та clip bounds. Для кастомної поведінки використовуйте TransitionSet з ChangeBounds, ChangeImageTransform, ChangeClipBounds. Наші інженери завжди налаштовують sharedElementReturnTransition окремо — наприклад, інший easing для зворотного шляху, щоб анімація не була дзеркальною. TransitionSet можна побудувати в XML або кодом, додаючи підтранзиції в порядку виконання.

Jetpack Compose: SharedTransitionLayout

Compose (починаючи з версії 1.7) приніс SharedTransitionLayout та SharedTransitionScope. Jetpack Compose вдвічі спрощує реалізацію для простих переходів порівняно з Fragment Transitions.

SharedTransitionLayout {
    NavHost(navController, startDestination = "list") {
        composable("list") {
            AnimatedVisibility(visible = true) {
                ProductList(
                    onProductClick = { product ->
                        navController.navigate("detail/${product.id}")
                    },
                    sharedTransitionScope = this@SharedTransitionLayout,
                    animatedVisibilityScope = this@AnimatedVisibility
                )
            }
        }
        composable("detail/{id}") { backStackEntry ->
            AnimatedVisibility(visible = true) {
                ProductDetail(
                    productId = backStackEntry.arguments?.getString("id"),
                    sharedTransitionScope = this@SharedTransitionLayout,
                    animatedVisibilityScope = this@AnimatedVisibility
                )
            }
        }
    }
}

// В ProductList:
@Composable
fun ProductCard(
    product: Product,
    sharedTransitionScope: SharedTransitionScope,
    animatedVisibilityScope: AnimatedVisibilityScope,
) {
    with(sharedTransitionScope) {
        AsyncImage(
            model = product.imageUrl,
            modifier = Modifier.sharedElement(
                rememberSharedContentState(key = "product-image-${product.id}"),
                animatedVisibilityScope
            )
        )
    }
}

Ключ має збігатися на обох екранах. sharedElement обробляє геометричний перехід, sharedBounds — коли контейнер також має плавно змінити розмір.

Таблиця типів анімації
Тип анімації Застосування
ChangeBounds Анімує позицію та розмір View
ChangeImageTransform Анімує трансформацію ImageView (наприклад, scaleType)
ChangeClipBounds Анімує обрізку за межами

Як налаштувати Shared Element для списку в RecyclerView?

При використанні RecyclerView кожному елементу потрібно задати унікальне ім'я через setTransitionName в onBindViewHolder. Зазвичай використовують ID елемента, наприклад "product_image_${product.id}". Якщо ім'я однакове, система не знає, який саме View анімувати. Також не забудьте встановити transitionName в приймаючому екрані. Обов'язково перевіряйте, щоб View не перекривався системною панеллю — для цього використовуйте ViewCompat.setOnApplyWindowInsetsListener.

Порівняння підходів: Fragment vs Compose

Критерій Fragment Transitions Jetpack Compose
API рівень Android 5.0+ Compose 1.7+
Підтримка асинхронного завантаження Вручну через postponeEnterTransition Автоматично через AsyncImage
Кастомні анімації TransitionSet, ChangeBounds, ChangeImageTransform sharedElement + sharedBounds, налаштування через graphicsLayer
Зворотна анімація sharedElementReturnTransition Автоматично при pop
Складність інтеграції Середня (потрібне керування transitionName) Низька (модифікатори, вдвічі простіше)

Типові помилки та як їх уникнути

  • transitionName задано лише на одному екрані — transition не працює мовчки. Завжди перевіряйте обидва напрямки.
  • RecyclerView з множиною елементів: setTransitionName потрібно викликати в onBindViewHolder з унікальним ім'ям для кожного item (наприклад, "product_image_${product.id}"). Якщо використовувати однакове ім'я — Android не знає, який саме view анімувати.
  • Shared Element поверх системної навігації (edge-to-edge): якщо view знаходиться близько до краю та content за navigation bar — WindowInsetsCompat може зміщувати layout, і фінальна позиція transition відрізняється від реальної. Вирішується через правильне застосування insets через ViewCompat.setOnApplyWindowInsetsListener. У наших проєктах ми завжди перевіряємо коректність позицій на пристроях з різними версіями Android.

Що входить в роботу

  • Аналіз поточної навігації та виділення спільних елементів
  • Проектування transitionName та ключів Compose
  • Реалізація з урахуванням асинхронних завантажень (Glide, Coil, AsyncImage)
  • Налаштування кастомних TransitionSet для нестандартних сценаріїв
  • Тестування на пристроях з Android 5.0+ (API 21) та edge-to-edge
  • Документація щодо підтримки та можливих регресій
  • Гарантія стабільної роботи протягом 30 днів після здачі

Строки

Shared Element Transition між двома екранами (зображення + заголовок): 1 день. З підтримкою асинхронного завантаження зображень, кастомним transition set та зворотною анімацією: 1–2 дні. Вартість простої реалізації починається від 300$, складнішої — до 800$. Якщо у вас складна навігація з кількома спільними елементами — зв'яжіться з нами, оцінимо проєкт протягом робочого дня.

Ми займаємося Android-розробкою понад 5 років, реалізували 50+ проєктів з анімаціями переходів. Наші інженери мають понад 5 років досвіду, виконали 50+ проєктів, 90% клієнтів рекомендують нас після завершення проєкту.

Хочете таку ж плавну навігацію у вашому додатку? Отримайте консультацію — просто напишіть нам. Якщо сумніваєтеся в реалізації — подивіться документацію по Shared Element Transition.

Анімації в мобільних додатках: Lottie, Rive, Spring та Reanimated

Ми зробили анімації для десятків проєктів — від ігрових інтерфейсів до bank-grade додатків. Знаємо, як змусити 120 fps працювати навіть на Android з ProGuard. Якщо анімація смикається — проблема не в інструменті, а у виборі підходу. Нижче покажемо, як обирати між Lottie та Rive, чому Spring physics перемагає UIView.animate, і як Reanimated 3 вичавлює 60 fps на старих пристроях. Отримайте консультацію щодо вашого проєкту — оцінимо анімаційний шар безкоштовно.

Чому 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:).

Як React Native Reanimated обходить JS-міст

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 може знижувати FPS на Android і як це виправити

Lottie експортує After Effects анімацію в JSON. На iOS з lottie-ios працює стабільно, але на Android при складних ефектах (blur, частинки, градієнти) векторний рендеринг через Canvas призводить до просадок до 30-40 fps. Рішення — або спрощувати анімацію, або використовувати Rive з апаратним рендерингом. Ми тестували: Lottie-файл з розмиттям 5 МБ на Xiaomi Redmi Note 10 дає 48 fps, аналогічна анімація в Rive (.riv 400 КБ) — 60 fps. Rive працює в 1.25 раза швидше на однаковому пристрої, а при складних ефектах — до 4 разів легше за розміром.

Як вибрати між Lottie та Rive для вашого проєкту?

Обидва інструменти вирішують задачу «дизайнер робить анімацію, розробник додає файл». Але принципово по-різному.

Критерій Lottie Rive
Формат JSON векторна анімація Бінарний .riv
Інтерактивність Ні (лінійне відтворення) State Machine, реакції на ввід
Продуктивність Середня (blur/частинки важкі) Апаратний рендеринг Metal/OpenGL
Розмір файлу 2-5 МБ 200-500 КБ
Підтримка платформ iOS, Android, Web, Flutter, RN iOS, Android, Web, Flutter, RN

Вибір простий: статична декоративна анімація (splash screen, onboarding ілюстрації) — Lottie. Інтерактивні UI-елементи зі станами — Rive. Наприклад, кнопка з hover, pressed, loading, success — одна Rive-анімація з чотирма станами проти чотирьох окремих Lottie-файлів. Це зменшує обсяг коду та спрощує підтримку.

Як впровадити анімації Lottie за 4 кроки?

  1. Експорт з After Effects через плагін Bodymovin — отримуєте .json.
  2. Додавання файлу до ресурсів проєкту (assets або drawable).
  3. Ініціалізація плеєра — в iOS використовуйте LOTAnimationView, в Android — LottieAnimationView.
  4. Запуск з параметрамиloopMode, animationSpeed, contentMode. Для складних сценаріїв — кастомний AnimationListener.

На практиці цей підхід працює, але потребує контролю версій Lottie-файлів. Ми рекомендуємо версіонувати їх разом з кодом і тестувати на цільових пристроях.

Spring-фізика та Hero transitions: як досягти природності

Spring-анімація відчувається природньо тому що імітує фізику — масу, жорсткість і демпфування. У SwiftUI: Animation.spring(response:dampingFraction:). В Android Compose: spring(dampingRatio = Spring.DampingRatioMediumBouncy).

Для Hero-переходів (елемент «перелітає» між екранами) на iOS використовуємо UIViewControllerTransitioningDelegate + UIViewControllerAnimatedTransitioning. У SwiftUI — matchedTransitionSource + navigationTransition(.zoom). На Flutter — Hero віджет, який працює з коробки.

Як уникнути типових помилок у Hero transitions?

Анімація починається нормально, але на цільовому екрані елемент «стрибає» в фінальну позицію. Причина — AutoLayout constraints застосовуються до завершення анімації. Рішення: layoutIfNeeded() в блоці анімації або використання transform замість frame-змін. На Flutter — перевіряйте HeroFlightShuttleConfiguration. Також слідкуйте за VSync: на Android при 90 Гц дисплеї Hero-перехід може виглядати смиканим, якщо не виставлений Choreographer.

Таблиця: Строки виконання та типові сценарії

Задача Час Коментар
Базові екранні переходи та мікроінтеракції 1 тиждень Стандартні gesture-driven sheets, каруселі
Lottie/Rive інтеграція з дизайн-системою 3-5 днів Після отримання фінальних файлів
Кастомна інтерактивність з фізикою (сustom sheet, drawer) 1-2 тижні Включає тестування на 4+ пристроях

Що входить в роботу: анімаційний шар під ключ

  • Інтеграція Lottie/Rive-файлів в дизайн-систему
  • Код gesture-driven переходів (bottom sheets, drawers, каруселі)
  • Тестування на реальних пристроях (iOS, Android)
  • Документація з анімацій (архітектура, ключі станів)
  • Підтримка при оновленні дизайну (гарантія 30 днів)

У нас 5+ років досвіду в мобільній розробці, понад 30 проєктів з анімаціями, сертифіковані iOS/Android розробники. Замовте анімації під ключ — перша консультація безкоштовна. Напишіть нам у месенджер, щоб узгодити деталі.

Apple Developer Documentation – UIKit Animations використовується як базова специфікація.