Timeline — основи, керування часом анімації в застосунках

Автор: IT Sectr Опубліковано: 2026-03-02 Час читання: 8 хв
Timeline (часова шкала) — фундаментальна концепція анімації, яка визначає, як довго триває анімація, в якому порядку виконуються її етапи і як час відображається на прогрес. У розробці мобільних застосунків часова шкала керується через тривалість (duration), затримку (delay), повторення (repeat) та часові мітки. В iOS анімаційна часова шкала реалізована через CACurrentMediaTime та CAMediaTiming, в Android — через ValueAnimator та Animator з параметрами duration, startDelay та repeatCount. За даними Apple Documentation, точне керування часовою шкалою критично важливе для синхронізації анімацій: розсинхронізація навіть на 16 мс (один кадр при 60fps) сприймається користувачем як смикання. В IT Sectr ми стандартизували використання часової шкали через ValueAnimator в Android та CABasicAnimation з CAMediaTiming в iOS — це забезпечує однакове керування часом у всіх проєктах.

Головне

  • Timeline — концепція керування часом анімації: duration, delay, repeat, відображення часу на прогрес.
  • CAMediaTiming — протокол iOS Core Animation для керування часовою шкалою: duration, beginTime, repeatCount, speed, timeOffset.
  • ValueAnimator — центральний клас Android для роботи з часовою шкалою анімації через duration, startDelay, repeatCount.
  • CACurrentMediaTime — функція iOS для отримання абсолютного часу в секундах, використовується для синхронізації.
  • TimeInterval — тип даних в iOS (Double), що представляє інтервал часу для анімаційних розрахунків.

Що таке Timeline?

Timeline — це модель часу анімації, яка визначає, коли анімація починається, як довго триває і як хід часу перетворюється на зміну анімованого параметра. На відміну від покадрової анімації (frame-by-frame), де кожен кадр задається вручну, анімація з часовою шкалою описується параметрами: duration — загальна тривалість, startDelay — затримка перед стартом, repeatCount — кількість повторень, та timingFunction — крива швидкості.

Часова шкала працює в нормалізованому просторі [0, 1] — локальний час або прогрес. Нульове значення відповідає початку анімації, одиниця — завершенню. Функція easing (згладжування) перетворює лінійний локальний час у фактичний прогрес анімації. Якщо repeatCount > 1, часова шкала циклічно скидається (RESTART) або інвертується (REVERSE). В iOS CAMediaTiming додає параметри speed (швидкість відтворення, >1 прискорює) та timeOffset (зсув часової шкали). В Android Animator використовує setCurrentPlayTime() для прямого керування позицією на часовій шкалі. За даними Material Design, анімації коротші за 100 мс не сприймаються як анімація, а довші за 500 мс — починають дратувати. Оптимальна тривалість для інтерфейсних анімацій — 200–350 мс.

Timeline в iOS: CAMediaTiming та CACurrentMediaTime

В iOS керування часовою шкалою анімації здійснюється через протокол CAMediaTiming, який реалізують CAAnimation та CALayer. Він надає властивості: duration (тривалість у секундах), beginTime (час старту відносно батьківської часової шкали), repeatCount (кількість повторень), repeatDuration (загальна тривалість повторень), autoreverses (автообернений хід), fillMode (поведінка до/після анімації), speed (швидкість відтворення) та timeOffset (зсув по шкалі).

CACurrentMediaTime() — функція, що повертає абсолютний час у секундах від останнього перезавантаження пристрою. Використовується як базова часова мітка для синхронізації кількох анімацій: встановіть beginTime = CACurrentMediaTime() + offset для запуску групи анімацій з точною затримкою. CAMediaTiming також підтримує ієрархію часових шкал — батьківський шар може прискорювати/уповільнювати всі дочірні анімації через властивість speed. В UIKit UIViewPropertyAnimator (iOS 10+) абстрагує часову шкалу через властивості duration, delay та колбеки startAnimation/pauseAnimation/stopAnimation. В SwiftUI структура Animation надає duration як частину специфікації анімації, але пряме керування часовою шкалою приховане — для спеціальної часової шкали використовуйте TimelineView (iOS 15+) з власним scheduler.

Timeline в Android: Animator та duration

В Android керування часовою шкалою централізовано в класі Animator та його нащадках (ValueAnimator, ObjectAnimator). Основні властивості часової шкали: duration (тривалість у мілісекундах), startDelay (затримка перед стартом), repeatCount (кількість повторень), repeatMode (RESTART або REVERSE). Метод setCurrentPlayTime(long) дозволяє переміщатися по часовій шкалі в довільну позицію.

ValueAnimator додає рівень абстракції: він генерує значення від 0 до 1 (нормалізований час) на основі duration та передає їх у TimeInterpolator. AnimatorSet дозволяє комбінувати кілька Animator в єдину часову шкалу з послідовним (playSequentially) або паралельним (playTogether) виконанням. В Jetpack Compose часова шкала керується через AnimationSpec: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) та keyframes (точки на часовій шкалі з власним easing для кожного відрізка). Compose AnimatedContent використовує часову шкалу для transitionSpec — розробник описує, як контент з'являється/зникає з часовими зсувами. За даними Android Performance, часова шкала анімації має бути кратною 16 мс (vsync-цикл) для 60fps — неправильна тривалість може викликати пропуск кадрів через невідповідність з частотою оновлення дисплея.

Композиція анімацій у часі

Часова шкала стає особливо важливою при композиції кількох анімацій. Три базові патерни: каскад — анімації запускаються послідовно зі зсувом (кожна наступна починається через offset після попередньої), паралель — всі анімації запускаються одночасно з однаковою часовою шкалою, та stagger — анімації запускаються з перекриттям (друга починається до завершення першої).

В iOS каскад реалізується через beginTime кожної CAAnimation = попередня beginTime + попередня duration. Для stagger використовується CAAnimationGroup з різними beginTime у вкладених анімаціях. В Android AnimatorSet підтримує playSequentially та playTogether; stagger створюється встановленням startDelay для кожного Animator. В Compose AnimatedVisibility з enter/exitTransition використовує stagger у варіанті staggerChildren для каскадної появи елементів списку. На WWDC та Google I/O 2024 обидві платформи представили інструменти візуалізації часової шкали: Xcode Animations Inspector та Android Studio Animations Timeline — вони показують фактичну часову шкалу та пропущені кадри в реальному часі.

Приклади коду

iOS: CABasicAnimation з CAMediaTiming

Приклад анімації зі спеціальною часовою шкалою: затримка 0,3 с, тривалість 0,6 с, autoreverse та нескінченне повторення. CAMediaTiming керує всіма часовими параметрами.

swift
import UIKit

func breathingAnimation(layer: CALayer) {
    let animation = CABasicAnimation(keyPath: "opacity")
    animation.duration = 0.6
    animation.beginTime = CACurrentMediaTime() + 0.3
    animation.fromValue = 1.0
    animation.toValue = 0.3
    animation.autoreverses = true
    animation.repeatCount = .infinity
    animation.speed = 1.0
    animation.fillMode = .both

    layer.add(animation, forKey: "breathing")
}

beginTime = CACurrentMediaTime() + 0,3 дає затримку 300 мс. autoreverses = true змушує анімацію йти в зворотному напрямку після кожного циклу. repeatCount = .infinity створює нескінченний цикл «дихаючого» ефекту. speed = 1,0 — нормальна швидкість; прискорте анімацію вдвічі значенням 2,0.

iOS: TimelineView для спеціальної часової шкали

SwiftUI TimelineView (iOS 15+) надає доступ до системної часової шкали для спеціальної анімації. View оновлюється при кожному тіку scheduler.

swift
import SwiftUI

struct TimelineViewExample: View {
    var body: some View {
        TimelineView(.periodic(from: .now, by: 0.1)) { context in
            let seconds = context.date.timeIntervalSinceReferenceDate
            let phase = sin(seconds * 2 * .pi)

            Circle()
                .fill(Color.blue)
                .frame(width: 60 + 30 * phase)
                .animation(.linear(duration: 0), value: phase)
        }
    }
}

TimelineView з .periodic scheduler генерує тіки кожні 0,1 с. У замиканні context.date — поточний час, який перетворюється на фазу синусоїди. Розмір кола пульсує з частотою 1 Гц. TimelineView — єдиний спосіб створити анімацію з власною часовою шкалою в SwiftUI без Animation API.

Android: ValueAnimator зі спеціальною часовою шкалою

ValueAnimator з повним керуванням часовою шкалою: затримка, тривалість, повторення. Метод setCurrentPlayTime дозволяє стрибати по часовій шкалі.

kotlin
import android.animation.ValueAnimator
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.View

fun View.timelineAnimation() {
    ValueAnimator.ofFloat(0f, 1f).apply {
        duration = 400
        startDelay = 200
        repeatCount = 2
        repeatMode = ValueAnimator.REVERSE
        interpolator = AccelerateDecelerateInterpolator()

        addUpdateListener { animator ->
            val progress = animator.animatedFraction
            alpha = progress
            translationX = progress * 100f
        }

        start()
    }
}

startDelay = 200 мс — затримка перед стартом. repeatMode = REVERSE повертає анімацію до початкового значення. animatedFraction дає прогрес від 0 до 1 з урахуванням repeatMode. addUpdateListener спрацьовує кожен кадр (зазвичай 16 мс). Для призупинення використовуйте pause() / resume().

Android: AnimatorSet для композиції часової шкали

AnimatorSet дозволяє створювати складні часові шкали з кількох анімацій. У прикладі — послідовне виконання з перекриттям (stagger).

kotlin
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.view.View

fun View.staggerAnimation() {
    val fadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).apply {
        duration = 300
    }
    val slideUp = ObjectAnimator.ofFloat(this, "translationY", 100f, 0f).apply {
        duration = 400
        startDelay = 100
    }
    val scale = ObjectAnimator.ofFloat(this, "scaleX", 0.8f, 1f).apply {
        duration = 250
        startDelay = 200
    }

    AnimatorSet().apply {
        playTogether(fadeIn, slideUp, scale)
        start()
    }
}

playTogether запускає всі анімації паралельно, але кожна має свій startDelay — це створює stagger-ефект: fadeIn стартує першим (0 мс), slideUp через 100 мс, scale через 200 мс. Підсумкова часова шкала: 0 — 100 — 200 — 600 мс. Для послідовного виконання використовуйте playSequentially.

Часто задавані питання

Чим Animator відрізняється від ValueAnimator в Android?

Animator — абстрактний базовий клас для всіх анімацій. ValueAnimator — його підклас, що анімує числові значення без прив'язки до об'єкта. ObjectAnimator (нащадок ValueAnimator) анімує конкретну властивість об'єкта (наприклад, alpha або translationY). Для часової шкали використовуйте ValueAnimator як універсальний таймер з колбеком через addUpdateListener.

Як синхронізувати анімації на різних пристроях?

Використовуйте системні годинники (CACurrentMediaTime в iOS, System.nanoTime в Android) для розрахунку прогресу, а не кількість кадрів. Для мережевої синхронізації застосовуйте NTP-базований таймер з корекцією offset. iOS підтримує AVAudioSession для синхронізації аудіо-відео часової шкали з точністю до мілісекунд. Уникайте синхронізації по кадрах — частота кадрів варіюється від 30 до 120 fps.

Що таке startOffset та repeatCount в Animator?

startOffset — затримка перед початком анімації в мілісекундах. Використовується для каскаду та stagger-ефектів. repeatCount — кількість повторень: 0 = не повторюється, ValueAnimator.INFINITE = нескінченний цикл. repeatMode визначає напрямок: RESTART (скидання до початку) або REVERSE (зворотний хід). В iOS startOffset називається beginTime (в комбінації з CACurrentMediaTime), repeatCount — однойменна властивість CABasicAnimation.

Яка оптимальна тривалість анімації?

Material Design рекомендує 200–350 мс для інтерфейсних анімацій. Анімації коротші за 100 мс не сприймаються як анімація, довші за 500 мс — дратують. Для входу/виходу елементів: 200–300 мс. Для переміщення між екранами: 300–400 мс. Використовуйте коротші тривалості для дрібних елементів (100–200 мс) і довші для великих (400–500 мс).

Чому анімація смикається при відтворенні?

Смикання (jank) виникає через: тривалі операції в головному потоці (блокують рендеринг), складну ієрархію View/Layer з overdraw, або неправильне використання часової шкали (тривалість не кратна vsync — 16ms для 60fps, 8ms для 120fps). Використовуйте Xcode Instruments Animations або Android Studio Profiler для виявлення пропущених кадрів. В Compose застосовуйте Modifier.drawWithCache для кешування складних відмальовок.

Підсумки

  • Timeline — модель часу анімації з параметрами duration, startDelay, repeatCount, repeatMode, що визначають хід анімації.
  • iOS CAMediaTiming протокол керує часовою шкалою через duration, beginTime, speed, timeOffset, repeatCount, fillMode.
  • Android Animator та ValueAnimator надають duration, startDelay, repeatCount, repeatMode та setCurrentPlayTime для прямого керування.
  • AnimatorSet в Android та CAAnimationGroup в iOS дозволяють комбінувати анімації в єдину часову шкалу.
  • TimelineView в SwiftUI (iOS 15+) дає доступ до системного scheduler для спеціальних часових шкал без Animation API.
  • CACurrentMediaTime() в iOS та System.nanoTime в Android — базові часові мітки для синхронізації анімацій.
  • Оптимальна тривалість інтерфейсних анімацій — 200-350 мс згідно з Material Design та HIG рекомендаціями.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також