Timeline — podstawy, zarządzanie czasem animacji w aplikacjach

Autor: IT Sectr Opublikowano: 2026-03-02 Czas czytania: 8 min
Timeline (oś czasu) — fundamentalna koncepcja animacji określająca jak długo trwa animacja, w jakiej kolejności wykonywane są jej etapy i jak czas jest odwzorowywany na postęp. W tworzeniu aplikacji mobilnych timeline jest zarządzany przez czas trwania (duration), opóźnienie (delay), powtórzenia (repeat) i znaczniki czasu. W iOS animacyjny timeline jest zaimplementowany przez CACurrentMediaTime i CAMediaTiming, w Androidzie — przez ValueAnimator i Animator z parametrami duration, startDelay i repeatCount. Według dokumentacji Apple, precyzyjne zarządzanie osią czasu jest krytyczne dla synchronizacji animacji: desynchronizacja nawet o 16 ms (jedna klatka przy 60fps) jest odbierana przez użytkownika jako szarpanie. W IT Sectr zestandaryzowaliśmy użycie timeline przez ValueAnimator w Androidzie i CABasicAnimation z CAMediaTiming w iOS — zapewnia to jednolite zarządzanie czasem we wszystkich projektach.

Najważniejsze

  • Timeline — koncepcja zarządzania czasem animacji: duration, delay, repeat, odwzorowanie czasu na postęp.
  • CAMediaTiming — protokół iOS Core Animation do zarządzania timeline: duration, beginTime, repeatCount, speed, timeOffset.
  • ValueAnimator — centralna klasa Android do pracy z osią czasu animacji przez duration, startDelay, repeatCount.
  • CACurrentMediaTime — funkcja iOS do pobierania bezwzględnego czasu w sekundach, używana do synchronizacji.
  • TimeInterval — typ danych w iOS (Double) reprezentujący interwał czasu dla obliczeń animacyjnych.

Czym jest Timeline?

Timeline — to model czasu animacji, który określa, kiedy animacja się zaczyna, jak długo trwa i jak upływ czasu jest przekształcany w zmianę animowanego parametru. W przeciwieństwie do animacji klatkowej (frame-by-frame), gdzie każda klatka jest ustawiana ręcznie, animacja timeline jest opisywana parametrami: duration — całkowity czas trwania, startDelay — opóźnienie przed startem, repeatCount — liczba powtórzeń, i timingFunction — krzywa prędkości.

Oś czasu działa w znormalizowanej przestrzeni [0, 1] — local time lub progress. Wartość zerowa odpowiada początkowi animacji, jedynka — zakończeniu. Funkcja easing przekształca liniowy local time w rzeczywisty postęp animacji. Jeśli repeatCount > 1, timeline cyklicznie resetuje się (RESTART) lub odwraca (REVERSE). W iOS CAMediaTiming dodaje parametry speed (prędkość odtwarzania, >1 przyspiesza) i timeOffset (przesunięcie osi czasu). W Androidzie Animator używa setCurrentPlayTime() do bezpośredniego zarządzania pozycją na osi czasu. Według Material Design, animacje krótsze niż 100 ms nie są odbierane jako animacja, a dłuższe niż 500 ms — zaczynają irytować. Optymalny czas trwania dla animacji interfejsu to 200–350 ms.

Timeline w iOS: CAMediaTiming i CACurrentMediaTime

W iOS zarządzanie osią czasu animacji odbywa się przez protokół CAMediaTiming, który implementują CAAnimation i CALayer. Udostępnia on właściwości: duration (czas trwania w sekundach), beginTime (czas startu względem nadrzędnego timeline), repeatCount (liczba powtórzeń), repeatDuration (całkowity czas trwania powtórzeń), autoreverses (automatyczny ruch wsteczny), fillMode (zachowanie przed/po animacji), speed (prędkość odtwarzania) i timeOffset (przesunięcie na osi).

CACurrentMediaTime() — funkcja zwracająca bezwzględny czas w sekundach od ostatniego restartu urządzenia. Używana jako podstawowy znacznik czasu do synchronizacji wielu animacji: ustaw beginTime = CACurrentMediaTime() + offset, aby uruchomić grupę animacji z dokładnym opóźnieniem. CAMediaTiming obsługuje również hierarchię timeline — nadrzędna warstwa może przyspieszać/spowalniać wszystkie podrzędne animacje przez właściwość speed. W UIKit UIViewPropertyAnimator (iOS 10+) abstrahuje oś czasu przez właściwości duration, delay i callbacki startAnimation/pauseAnimation/stopAnimation. W SwiftUI struktura Animation udostępnia duration jako część specyfikacji animacji, ale bezpośrednie zarządzanie osią czasu jest ukryte — do niestandardowego timeline użyj TimelineView (iOS 15+) z własnym schedulerem.

Timeline w Android: Animator i duration

W Androidzie zarządzanie osią czasu jest scentralizowane w klasie Animator i jej dziedzicach (ValueAnimator, ObjectAnimator). Podstawowe właściwości timeline: duration (czas trwania w milisekundach), startDelay (opóźnienie przed startem), repeatCount (liczba powtórzeń), repeatMode (RESTART lub REVERSE). Metoda setCurrentPlayTime(long) pozwala poruszać się po osi czasu w dowolnej pozycji.

ValueAnimator dodaje poziom abstrakcji: generuje wartości od 0 do 1 (normalized time) na podstawie duration i przekazuje je do TimeInterpolator. AnimatorSet pozwala łączyć wiele Animator w jedną oś czasu z sekwencyjnym (playSequentially) lub równoległym (playTogether) wykonaniem. W Jetpack Compose oś czasu jest zarządzana przez AnimationSpec: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) i keyframes (punkty na osi czasu z niestandardowym easing dla każdego odcinka). Compose AnimatedContent używa timeline dla transitionSpec — programista opisuje, jak treść pojawia się/znika z przesunięciami czasowymi. Według Android Performance, oś czasu animacji powinna być wielokrotnością 16 ms (cykl vsync) dla 60fps — nieprawidłowy czas trwania może powodować pomijanie klatek z powodu niezgodności z częstotliwością odświeżania ekranu.

Kompozycja animacji w czasie

Oś czasu staje się szczególnie ważna przy kompozycji wielu animacji. Trzy podstawowe wzorce: kaskada — animacje uruchamiane sekwencyjnie z przesunięciem (każda następna zaczyna się po offset od poprzedniej), równoległość — wszystkie animacje uruchamiane jednocześnie z tym samym timeline, i stagger — animacje uruchamiane z nakładaniem (druga zaczyna się przed zakończeniem pierwszej).

W iOS kaskada jest implementowana przez beginTime każdej CAAnimation = poprzednia beginTime + poprzednia duration. Dla stagger używa się CAAnimationGroup z różnymi beginTime w zagnieżdżonych animacjach. W Androidzie AnimatorSet obsługuje playSequentially i playTogether; stagger tworzy się przez ustawienie startDelay dla każdego Animator. W Compose AnimatedVisibility z enter/exitTransition używa stagger w wariancie staggerChildren do kaskadowego pojawiania się elementów listy. Na WWDC i Google I/O 2024 obie platformy przedstawiły narzędzia wizualizacji timeline: Xcode Animations Inspector i Android Studio Animations Timeline — pokazują one rzeczywistą oś czasu i pominięte klatki w czasie rzeczywistym.

Przykłady kodu

iOS: CABasicAnimation z CAMediaTiming

Przykład animacji z niestandardowym timeline: opóźnienie 0.3 sek, czas trwania 0.6 sek, autoreverse i nieskończone powtórzenie. CAMediaTiming zarządza wszystkimi parametrami czasowymi.

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 daje opóźnienie 300 ms. autoreverses = true powoduje, że animacja idzie w przeciwnym kierunku po każdym cyklu. repeatCount = .infinity tworzy nieskończoną pętlę efektu „oddychania”. speed = 1.0 — normalna prędkość; przyspiesz animację 2 razy wartością 2.0.

iOS: TimelineView dla niestandardowej osi czasu

SwiftUI TimelineView (iOS 15+) zapewnia dostęp do systemowego timeline dla niestandardowej animacji. Widok aktualizuje się przy każdym tiknięciu schedulera.

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 z .periodic scheduler generuje tiknięcia co 0.1 sek. W zamknięciu context.date — bieżący czas, który jest przekształcany w fazę sinusoidy. Rozmiar okręgu pulsuje z częstotliwością 1 Hz. TimelineView — jedyny sposób na stworzenie animacji z własną osią czasu w SwiftUI bez Animation API.

Android: ValueAnimator z niestandardowym timeline

ValueAnimator z pełnym zarządzaniem osią czasu: opóźnienie, czas trwania, powtórzenie. Metoda setCurrentPlayTime pozwala skakać po osi czasu.

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 ms — opóźnienie przed startem. repeatMode = REVERSE zwraca animację do wartości początkowej. animatedFraction daje postęp od 0 do 1 z uwzględnieniem repeatMode. addUpdateListener wywołuje się co klatkę (zwykle 16 ms). Do wstrzymania użyj pause() / resume().

Android: AnimatorSet do kompozycji timeline

AnimatorSet pozwala tworzyć złożone osie czasu z wielu animacji. W przykładzie — sekwencyjne wykonanie z nakładaniem (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 uruchamia wszystkie animacje równolegle, ale każda ma swój startDelay — tworzy to efekt stagger: fadeIn startuje pierwszy (0 ms), slideUp po 100 ms, scale po 200 ms. Wynikowa oś czasu: 0 — 100 — 200 — 600 ms. Do sekwencyjnego wykonania użyj playSequentially.

Często zadawane pytania

Czym różni się Animator od ValueAnimator w Androidzie?

Animator — abstrakcyjna klasa bazowa dla wszystkich animacji. ValueAnimator — jej podklasa animująca wartości liczbowe bez powiązania z obiektem. ObjectAnimator (dziedzic ValueAnimator) animuje konkretną właściwość obiektu (np. alpha lub translationY). Do timeline używaj ValueAnimator jako uniwersalnego timera z callbackiem przez addUpdateListener.

Jak synchronizować animacje na różnych urządzeniach?

Używaj zegarów systemowych (CACurrentMediaTime w iOS, System.nanoTime w Android) do obliczania postępu, a nie liczby klatek. Do synchronizacji sieciowej stosuj timer NTP-based z korekcją offset. iOS obsługuje AVAudioSession do synchronizacji timeline audio-wideo z dokładnością do milisekund. Unikaj synchronizacji po klatkach — częstotliwość klatek waha się od 30 do 120 fps.

Czym jest startOffset i repeatCount w Animator?

startOffset — opóźnienie przed rozpoczęciem animacji w milisekundach. Używane do kaskady i efektów stagger. repeatCount — liczba powtórzeń: 0 = brak powtórzeń, ValueAnimator.INFINITE = nieskończona pętla. repeatMode określa kierunek: RESTART (reset do początku) lub REVERSE (ruch wsteczny). W iOS startOffset nazywa się beginTime (w kombinacji z CACurrentMediaTime), repeatCount — to samo właściwość CABasicAnimation.

Jaki jest optymalny czas trwania animacji?

Material Design zaleca 200–350 ms dla animacji interfejsu. Animacje krótsze niż 100 ms nie są odbierane jako animacja, dłuższe niż 500 ms — irytują. Dla wejścia/wyjścia elementów: 200–300 ms. Dla przejść między ekranami: 300–400 ms. Używaj krótszych czasów dla małych elementów (100–200 ms) i dłuższych dla dużych (400–500 ms).

Dlaczego animacja szarpie podczas odtwarzania?

Szarpnięcia (jank) powstają z powodu: długotrwałych operacji w głównym wątku (blokują renderowanie), złożonej hierarchii View/Layer z overdraw, lub nieprawidłowego użycia timeline (czas trwania nie jest wielokrotnością vsync — 16ms dla 60fps, 8ms dla 120fps). Używaj Xcode Instruments Animations lub Android Studio Profiler do wykrywania pominiętych klatek. W Compose stosuj Modifier.drawWithCache do cache'owania złożonych rysunków.

Podsumowanie

  • Timeline — model czasu animacji z parametrami duration, startDelay, repeatCount, repeatMode, określający przebieg animacji.
  • iOS CAMediaTiming protokół zarządza osią czasu przez duration, beginTime, speed, timeOffset, repeatCount, fillMode.
  • Android Animator i ValueAnimator udostępniają duration, startDelay, repeatCount, repeatMode i setCurrentPlayTime do bezpośredniego zarządzania.
  • AnimatorSet w Androidzie i CAAnimationGroup w iOS pozwalają łączyć animacje w jedną oś czasu.
  • TimelineView w SwiftUI (iOS 15+) daje dostęp do systemowego schedulera dla niestandardowych osi czasu bez Animation API.
  • CACurrentMediaTime() w iOS i System.nanoTime w Android — podstawowe znaczniki czasu do synchronizacji animacji.
  • Optymalny czas trwania animacji interfejsu — 200-350 ms zgodnie z zaleceniami Material Design i HIG.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również