Реалізація анімації Confetti/Celebration у мобільному додатку

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

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

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Реалізація анімації Confetti/Celebration у мобільному додатку
Середній
від 4 годин до 2 днів
Часті запитання

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

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

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

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    858
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    744
  • 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

Після завершення ключової дії у додатку користувач очікує візуального відгуку. Без анімації тріумфу емоція гасне, а retention падає. Confetti-анімація — це інженерне завдання: синтезувати десятки частинок, не викликавши просадок FPS і не порушивши UX. Наші інженери з 10-річним досвідом та понад 40 успішними проектами з інтеграції particle effects гарантують плавну анімацію. Зв'яжіться з нами, щоб обговорити інтеграцію під ключ.

Технічні складності рендерингу confetti

Основна проблема — рендеринг багатьох частинок без просадок FPS. На iOS CAEmitterLayer вирішує це апаратно, але неправильні параметри (надто високий birthRate, складні форми) можуть навантажити навіть флагмани. На Android кастомний Canvas.onDraw() — вузьке місце: кожне перемальовування синхронне з UI-потоком. При 200+ частинках потрібен SurfaceView або TextureView з окремим потоком. У Flutter CustomPainter працює на Dart-потоці, але при активній фізиці може викликати затримки.

Друга складність — узгодження з платформенними гайдлайнами. App Store Review Guidelines (Section 4.2) вимагають, щоб анімація не заважала основному функціоналу. Для Android важливо не блокувати back-button. Ми враховуємо ці нюанси на етапі проектування.

iOS: CAEmitterLayer — нативний підхід

CAEmitterLayer — правильний інструмент для particle effects на iOS. Як зазначено в документації Apple, це рекомендований спосіб. Рендеринг через Metal, анімація на render thread — main thread не бере участі.

func startConfetti(in view: UIView) {
    let emitter = CAEmitterLayer()
    emitter.emitterPosition = CGPoint(x: view.bounds.midX, y: -10)
    emitter.emitterShape = .line
    emitter.emitterSize = CGSize(width: view.bounds.width, height: 0)

    let colors: [UIColor] = [.systemRed, .systemBlue, .systemYellow, .systemGreen, .systemPurple]
    let shapes = ["square", "circle", "triangle"]

    emitter.emitterCells = colors.flatMap { color in
        shapes.map { _ in
            let cell = CAEmitterCell()
            cell.contents = UIImage(systemName: "circle.fill")?.withTintColor(color).cgImage
            cell.birthRate = 8
            cell.lifetime = 4.0
            cell.velocity = 200
            cell.velocityRange = 100
            cell.emissionLongitude = .pi
            cell.emissionRange = .pi / 4
            cell.spin = 3.5
            cell.spinRange = 1.0
            cell.scaleRange = 0.5
            cell.scale = 0.4
            cell.color = color.cgColor
            cell.alphaSpeed = -0.15
            return cell
        }
    }

    view.layer.addSublayer(emitter)

    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
        emitter.birthRate = 0
    }
    DispatchQueue.main.asyncAfter(deadline: .now() + 6.0) {
        emitter.removeFromSuperlayer()
    }
}

CAEmitterCell.birthRate — частинок в секунду на комірку. При 5 кольорах × 3 формах × 8 частинок/с = 120 частинок/с сумарно. На 4 секунди lifetime — до 480 частинок одночасно на екрані. На iPhone 12+ — комфортно, FPS не падає нижче 58 кадрів/с. На iPhone SE 2nd gen — починає грітися при 480 частинках; знижуємо birthRate до 4–5 для mid-range пристроїв.

Для кастомних форм confetti (прямокутники, зірки): створюємо UIGraphicsImageRenderer, малюємо форму, конвертуємо в CGImage для cell.contents.

Детальні параметри CAEmitterCell - `birthRate`: кількість частинок в секунду (рекомендується 4–8 для confetti) - `lifetime`: час життя частинки (сек) - `velocity`: початкова швидкість в пікселях/с - `emissionLongitude`: кут вильоту в радіанах - `spin`: початкове обертання в радіанах/с - `scale`: масштаб частинки

Порівняння: CAEmitterLayer vs SpriteKit vs готові бібліотеки

Рішення Продуктивність Гнучкість Складність
CAEmitterLayer Висока (Metal) Середня (тільки частинки) Низька
SpriteKit Середня (CPU/GPU) Висока (фізика, сцени) Середня
ConfettiSwiftUI Висока (обгортка над CAEmitter) Низька (обмежені параметри) Дуже низька

Android: Canvas-based або бібліотека

Кастомний ConfettiView через Canvas:

class ConfettiView(context: Context) : View(context) {
    private val particles = mutableListOf<ConfettiParticle>()
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var animator: ValueAnimator? = null

    data class ConfettiParticle(
        var x: Float, var y: Float,
        var vx: Float, var vy: Float,
        val color: Int,
        val size: Float,
        var rotation: Float,
        val rotationSpeed: Float,
        var alpha: Float = 1f
    )

    fun start() {
        repeat(80) {
            particles.add(ConfettiParticle(
                x = Random.nextFloat() * width,
                y = -Random.nextFloat() * 100,
                vx = Random.nextFloat() * 6 - 3,
                vy = Random.nextFloat() * 4 + 3,
                color = listOf(Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN).random(),
                size = Random.nextFloat() * 12 + 6,
                rotation = Random.nextFloat() * 360,
                rotationSpeed = Random.nextFloat() * 6 - 3
            ))
        }

        animator = ValueAnimator.ofFloat(0f, 1f).apply {
            duration = 5000
            addUpdateListener {
                updateParticles()
                invalidate()
            }
            start()
        }
    }

    private fun updateParticles() {
        particles.forEach { p ->
            p.x += p.vx
            p.vy += 0.1f
            p.y += p.vy
            p.rotation += p.rotationSpeed
            if (p.y > height * 0.7f) p.alpha -= 0.02f
        }
        particles.removeAll { it.alpha <= 0 || it.y > height + 50 }
    }

    override fun onDraw(canvas: Canvas) {
        particles.forEach { p ->
            paint.color = p.color
            paint.alpha = (p.alpha * 255).toInt()
            canvas.save()
            canvas.translate(p.x, p.y)
            canvas.rotate(p.rotation)
            canvas.drawRect(-p.size/2, -p.size/2, p.size/2, p.size/2, paint)
            canvas.restore()
        }
    }
}

ValueAnimator + invalidate() — це перемальовування Canvas на кожному кадрі. При 80 частинках завантаження CPU становить менше 10% на Snapdragon 8xx. При 200+ частинках зі складними формами — переходимо на SurfaceView з окремим render thread.

Готова бібліотека: nl.dionsegijn:konfetti:2.0.4 — пристойна, підтримує кастомні форми та KonfettiView.

Порівняння: кастомний Canvas vs konfetti

Рішення Продуктивність Гнучкість Залежності
Кастомний Canvas Середня (залежить від частинок) Висока Немає
konfetti Висока (оптимізований) Середня Одна бібліотека

Flutter

// Через CustomPainter
class ConfettiPainter extends CustomPainter {
  final List<ConfettiParticle> particles;
  ConfettiPainter(this.particles);

  @override
  void paint(Canvas canvas, Size size) {
    for (final p in particles) {
      final paint = Paint()..color = p.color.withOpacity(p.alpha);
      canvas.save();
      canvas.translate(p.x, p.y);
      canvas.rotate(p.rotation);
      canvas.drawRect(Rect.fromCenter(center: Offset.zero, width: p.size, height: p.size * 0.5), paint);
      canvas.restore();
    }
  }

  @override
  bool shouldRepaint(ConfettiPainter old) => true;
}

У Flutter простіше використовувати готовий пакет confetti: ^0.7.0 — там є ConfettiController з play(), stop(), параметри для напрямку, кольорів, форм.

Як інтегрувати confetti за 4 кроки?

  1. Визначте сценарії виклику: після покупки, завершення рівня, підписки.
  2. Виберіть платформу та стек: нативний модуль або кроссплатформне рішення.
  3. Реалізуйте модуль confetti з урахуванням дизайну та оптимізації.
  4. Протестуйте на реальних пристроях різних сегментів.

Чому CAEmitterLayer у 2–3 рази продуктивніший за SpriteKit?

CAEmitterLayer рендерить частинки через Metal на GPU, повністю минаючи main thread. SpriteKit використовує окремий процес, але потребує більше пам'яті для сцен. Для простих частинок (без фізики) CAEmitterLayer ефективніший на 60% по CPU.

Чому важлива оптимізація під різні пристрої?

Confetti з 480 частинок на iPhone SE 2nd gen може викликати троттлінг та перегрів. На флагманах це непомітно. Наша практика показує, що для mid-range Android-пристроїв (Snapdragon 6xx) безпечний ліміт — 120 частинок. Ми автоматично визначаємо пристрій і зменшуємо birthRate або використовуємо менш ресурсоємні форми. Гарантуємо, що анімація не підвісить UI.

Як ми забезпечуємо якість та гарантії?

Ми пройшли модерацію понад 40 додатків з анімаціями. Наші інженери сертифіковані з iOS та Android (Apple Certified iOS Developer, Google Associate Android Developer). Надаємо гарантію на інтеграцію: безкоштовна підтримка 30 днів після деплою. Замовте консультацію — оцінимо ваш проект.

Етапи роботи та що входить

Етап Що робимо Результат
Аналітика Визначаємо сценарії виклику confetti, вимоги до форм та поведінки Технічне завдання
Проектування Вибираємо стек, проектуємо архітектуру (модуль конфетті) Діаграма компонентів
Реалізація Пишемо код, інтегруємо в додаток Працюючий прототип
Тестування Профілюємо на реальних пристроях (5+ моделей), виправляємо баги Звіт тестування
Деплой Публікуємо в сторах, налаштовуємо прапорці включення Доступ до білду

Зазначимо: Що входить в роботу:

  • Вихідний код модуля confetti
  • Документація по API та кастомізації
  • Налаштування push-сповіщень, якщо confetti прив'язана до подій
  • Навчання ваших розробників (1 година онлайн)
  • Підтримка 30 днів після здачі

Терміни та вартість

Базова інтеграція через готову бібліотеку — 4–8 годин. Кастомна реалізація з унікальними формами, фізикою та оптимізацією під різні пристрої — від 1 до 3 днів. Вартість розраховується індивідуально. Отримайте консультацію — ми запропонуємо оптимальне рішення.

Анімації в мобільних додатках: 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 використовується як базова специфікація.