Timeline — bazele, gestionarea timpului animației în aplicații

Autor: IT Sectr Publicat: 2026-03-02 Timp de citire: 8 min
Timeline (axa timpului) — un concept fundamental al animației care definește cât durează animația, în ce ordine sunt executate etapele sale și cum timpul este reflectat asupra progresului. În dezvoltarea aplicațiilor mobile, timeline-ul este gestionat prin durată (duration), întârziere (delay), repetiții (repeat) și marcaje temporale. În iOS, timeline-ul de animație este implementat prin CACurrentMediaTime și CAMediaTiming, în Android — prin ValueAnimator și Animator cu parametrii duration, startDelay și repeatCount. Potrivit Documentației Apple, gestionarea precisă a axei timpului este critică pentru sincronizarea animațiilor: desincronizarea chiar și de 16 ms (un cadru la 60fps) este percepută de utilizator ca sacadare. La IT Sectr am standardizat utilizarea timeline-ului prin ValueAnimator în Android și CABasicAnimation cu CAMediaTiming în iOS — aceasta asigură o gestionare uniformă a timpului în toate proiectele.

Principalele

  • Timeline — conceptul gestionării timpului de animație: duration, delay, repeat, reflectarea timpului asupra progresului.
  • CAMediaTiming — protocolul iOS Core Animation pentru gestionarea timeline-ului: duration, beginTime, repeatCount, speed, timeOffset.
  • ValueAnimator — clasa centrală Android pentru lucrul cu axa timpului animației prin duration, startDelay, repeatCount.
  • CACurrentMediaTime — funcția iOS pentru obținerea timpului absolut în secunde, utilizată pentru sincronizare.
  • TimeInterval — tipul de date în iOS (Double) care reprezintă intervalul de timp pentru calcule de animație.

Ce este Timeline?

Timeline — este un model de timp al animației care definește când începe animația, cât durează și cum trecerea timpului este transformată în modificarea parametrului animat. Spre deosebire de animația cadru cu cadru (frame-by-frame), unde fiecare cadru este setat manual, animația timeline este descrisă prin parametri: duration — durata totală, startDelay — întârzierea înainte de start, repeatCount — numărul de repetări și timingFunction — curba vitezei.

Axa timpului funcționează în spațiul normalizat [0, 1] — local time sau progress. Valoarea zero corespunde începutului animației, iar unu — finalului. Funcția de easing transformă local time liniar în progresul real al animației. Dacă repeatCount > 1, timeline-ul se resetează ciclic (RESTART) sau se inversează (REVERSE). În iOS, CAMediaTiming adaugă parametrii speed (viteza de redare, >1 accelerează) și timeOffset (deplasarea axei timpului). În Android, Animator folosește setCurrentPlayTime() pentru gestionarea directă a poziției pe axa timpului. Potrivit Material Design, animațiile mai scurte de 100 ms nu sunt percepute ca animație, iar cele mai lungi de 500 ms — încep să irite. Durata optimă pentru animațiile de interfață este de 200–350 ms.

Timeline în iOS: CAMediaTiming și CACurrentMediaTime

În iOS, gestionarea axei timpului animației se realizează prin protocolul CAMediaTiming, implementat de CAAnimation și CALayer. Acesta oferă proprietățile: duration (durata în secunde), beginTime (timpul de start față de timeline-ul părinte), repeatCount (numărul de repetări), repeatDuration (durata totală a repetărilor), autoreverses (mișcarea automată inversă), fillMode (comportamentul înainte/după animație), speed (viteza de redare) și timeOffset (deplasarea pe axă).

CACurrentMediaTime() — funcția care returnează timpul absolut în secunde de la ultima repornire a dispozitivului. Utilizată ca marcaj temporal de bază pentru sincronizarea mai multor animații: setați beginTime = CACurrentMediaTime() + offset pentru a porni un grup de animații cu o întârziere precisă. CAMediaTiming suportă, de asemenea, ierarhia timeline-ului — stratul părinte poate accelera/încetini toate animațiile copil prin proprietatea speed. În UIKit, UIViewPropertyAnimator (iOS 10+) abstractizează axa timpului prin proprietățile duration, delay și callback-urile startAnimation/pauseAnimation/stopAnimation. În SwiftUI, structura Animation oferă duration ca parte a specificației animației, dar gestionarea directă a axei timpului este ascunsă — pentru un timeline personalizat utilizați TimelineView (iOS 15+) cu propriul scheduler.

Timeline în Android: Animator și duration

În Android, gestionarea axei timpului este centralizată în clasa Animator și în moștenitorii săi (ValueAnimator, ObjectAnimator). Proprietățile principale ale timeline-ului: duration (durata în milisecunde), startDelay (întârzierea înainte de start), repeatCount (numărul de repetări), repeatMode (RESTART sau REVERSE). Metoda setCurrentPlayTime(long) permite deplasarea pe axa timpului în orice poziție.

ValueAnimator adaugă un nivel de abstractizare: generează valori de la 0 la 1 (normalized time) pe baza duration și le transmite către TimeInterpolator. AnimatorSet permite combinarea mai multor Animator într-o singură axă a timpului cu execuție secvențială (playSequentially) sau paralelă (playTogether). În Jetpack Compose, axa timpului este gestionată prin AnimationSpec: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) și keyframes (puncte pe axa timpului cu easing personalizat pentru fiecare segment). Compose AnimatedContent folosește timeline-ul pentru transitionSpec — dezvoltatorul descrie cum conținutul apare/dispare cu deplasări temporale. Potrivit Android Performance, axa timpului animației trebuie să fie un multiplu de 16 ms (ciclul vsync) pentru 60fps — o durată incorectă poate cauza omiterea cadrelor din cauza nepotrivirii cu rata de reîmprospătare a ecranului.

Compoziția animațiilor în timp

Axa timpului devine deosebit de importantă la compoziția mai multor animații. Trei modele de bază: cascadă — animațiile sunt pornite secvențial cu deplasare (fiecare următoare începe după offset de la precedenta), paralel — toate animațiile sunt pornite simultan cu același timeline și stagger — animațiile sunt pornite cu suprapunere (a doua începe înainte de finalizarea primei).

În iOS, cascada este implementată prin beginTime al fiecărei CAAnimation = beginTime anterioară + duration anterioară. Pentru stagger se folosește CAAnimationGroup cu diferite beginTime în animații imbricate. În Android, AnimatorSet suportă playSequentially și playTogether; stagger-ul se creează prin setarea startDelay pentru fiecare Animator. În Compose, AnimatedVisibility cu enter/exitTransition folosește stagger în varianta staggerChildren pentru apariția cascadată a elementelor listei. La WWDC și Google I/O 2024, ambele platforme au prezentat instrumente de vizualizare a timeline-ului: Xcode Animations Inspector și Android Studio Animations Timeline — acestea arată axa timpului reală și cadrele omise în timp real.

Exemple de cod

iOS: CABasicAnimation cu CAMediaTiming

Exemplu de animație cu timeline personalizat: întârziere 0.3 sec, durată 0.6 sec, autoreverse și repetare infinită. CAMediaTiming gestionează toți parametrii temporali.

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 oferă o întârziere de 300 ms. autoreverses = true face ca animația să meargă în direcția opusă după fiecare ciclu. repeatCount = .infinity creează o buclă infinită a efectului de "respirație". speed = 1.0 — viteză normală; accelerați animația de 2 ori cu valoarea 2.0.

iOS: TimelineView pentru axa timpului personalizată

SwiftUI TimelineView (iOS 15+) oferă acces la timeline-ul sistemului pentru animație personalizată. Vederea se actualizează la fiecare tick al scheduler-ului.

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 cu scheduler .periodic generează tick-uri la fiecare 0.1 sec. în închidere, context.date — timpul curent care este transformat în faza sinusoidei. Dimensiunea cercului pulsează cu frecvența de 1 Hz. TimelineView este singura modalitate de a crea o animație cu propria axă a timpului în SwiftUI fără Animation API.

Android: ValueAnimator cu timeline personalizat

ValueAnimator cu gestionarea completă a axei timpului: întârziere, durată, repetare. Metoda setCurrentPlayTime permite sărirea pe axa timpului.

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 — întârzierea înainte de start. repeatMode = REVERSE readuce animația la valoarea inițială. animatedFraction oferă progresul de la 0 la 1 ținând cont de repeatMode. addUpdateListener se declanșează la fiecare cadru (de obicei 16 ms). Pentru pauză utilizați pause() / resume().

Android: AnimatorSet pentru compoziția timeline-ului

AnimatorSet permite crearea unor axe ale timpului complexe din mai multe animații. În exemplu — execuție secvențială cu suprapunere (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 pornește toate animațiile în paralel, dar fiecare are propriul startDelay — aceasta creează efectul de stagger: fadeIn pornește primul (0 ms), slideUp după 100 ms, scale după 200 ms. Axa timpului rezultată: 0 — 100 — 200 — 600 ms. Pentru execuție secvențială utilizați playSequentially.

Întrebări frecvente

Cu ce diferă Animator de ValueAnimator în Android?

Animator — clasa de bază abstractă pentru toate animațiile. ValueAnimator — subclasa sa care animează valori numerice fără legătură cu un obiect. ObjectAnimator (moștenitorul ValueAnimator) animează o proprietate specifică a obiectului (de exemplu, alpha sau translationY). Pentru timeline, utilizați ValueAnimator ca timer universal cu callback prin addUpdateListener.

Cum sincronizez animațiile pe diferite dispozitive?

Utilizați ceasurile de sistem (CACurrentMediaTime în iOS, System.nanoTime în Android) pentru calcularea progresului, nu numărul de cadre. Pentru sincronizarea în rețea utilizați un timer bazat pe NTP cu corecție offset. iOS suportă AVAudioSession pentru sincronizarea timeline-ului audio-video cu precizie de milisecunde. Evitați sincronizarea pe cadre — rata de cadre variază de la 30 la 120 fps.

Ce este startOffset și repeatCount în Animator?

startOffset — întârzierea înainte de începerea animației în milisecunde. Folosit pentru cascadă și efecte stagger. repeatCount — numărul de repetări: 0 = fără repetare, ValueAnimator.INFINITE = buclă infinită. repeatMode determină direcția: RESTART (resetare la început) sau REVERSE (mișcare inversă). În iOS, startOffset se numește beginTime (în combinație cu CACurrentMediaTime), repeatCount — proprietatea omonimă a CABasicAnimation.

Care este durata optimă a animației?

Material Design recomandă 200–350 ms pentru animațiile de interfață. Animațiile mai scurte de 100 ms nu sunt percepute ca animație, cele mai lungi de 500 ms — iritează. Pentru intrarea/ieșirea elementelor: 200–300 ms. Pentru tranziții între ecrane: 300–400 ms. Utilizați durate mai scurte pentru elemente mici (100–200 ms) și mai lungi pentru cele mari (400–500 ms).

De ce animația sacadează în timpul redării?

Sacadarea (jank) apare din cauza: operațiilor lungi în firul principal (blochează randarea), a ierarhiei complexe View/Layer cu overdraw, sau a utilizării incorecte a timeline-ului (durata nu este un multiplu de vsync — 16ms pentru 60fps, 8ms pentru 120fps). Utilizați Xcode Instruments Animations sau Android Studio Profiler pentru detectarea cadrelor omise. în Compose, aplicați Modifier.drawWithCache pentru cache-ul desenelor complexe.

Rezumat

  • Timeline — modelul de timp al animației cu parametrii duration, startDelay, repeatCount, repeatMode, care determină desfășurarea animației.
  • Protocolul iOS CAMediaTiming gestionează axa timpului prin duration, beginTime, speed, timeOffset, repeatCount, fillMode.
  • Android Animator și ValueAnimator oferă duration, startDelay, repeatCount, repeatMode și setCurrentPlayTime pentru gestionare directă.
  • AnimatorSet în Android și CAAnimationGroup în iOS permit combinarea animațiilor într-o singură axă a timpului.
  • TimelineView în SwiftUI (iOS 15+) oferă acces la scheduler-ul sistemului pentru axe ale timpului personalizate fără Animation API.
  • CACurrentMediaTime() în iOS și System.nanoTime în Android — marcaje temporale de bază pentru sincronizarea animațiilor.
  • Durata optimă a animațiilor de interfață — 200-350 ms conform recomandărilor Material Design și HIG.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și