Főbb pontok
Timeline — az animáció időmodellje, amely meghatározza, hogy az animáció mikor kezdődik, meddig tart és hogyan alakul az idő előrehaladása az animált paraméter változásává. A kockánkénti (frame-by-frame) animációval ellentétben, ahol minden kockát kézzel állítanak be, a timeline animáció paraméterekkel írható le: duration — teljes időtartam, startDelay — késleltetés az indítás előtt, repeatCount — ismétlések száma és timingFunction — sebességgörbe.
Az idővonal normalizált [0, 1] térben működik — local time vagy progress. A nulla érték az animáció kezdetének, az egy — a végének felel meg. Az easing függvény a lineáris local time-ot tényleges animációs előrehaladássá alakítja. Ha repeatCount > 1, a timeline ciklikusan visszaállítódik (RESTART) vagy megfordul (REVERSE). iOS-ben a CAMediaTiming hozzáadja a speed (lejátszási sebesség, >1 gyorsít) és timeOffset (idővonal eltolása) paramétereket. Android-ban az Animator a setCurrentPlayTime() függvényt használja az idővonalon lévő pozíció közvetlen kezeléséhez. A Material Design szerint a 100 ms-nál rövidebb animációk nem érzékelhetők animációként, az 500 ms-nál hosszabbak pedig — irritálni kezdenek. Az interfész-animációk optimális időtartama 200–350 ms.
iOS-ben az animációs idővonal kezelése a CAMediaTiming protokollon keresztül történik, amelyet a CAAnimation és a CALayer valósít meg. A következő tulajdonságokat biztosítja: duration (időtartam másodpercben), beginTime (kezdőidő a szülő timeline-hoz képest), repeatCount (ismétlések száma), repeatDuration (ismétlések teljes időtartama), autoreverses (automatikus visszirányú mozgás), fillMode (viselkedés animáció előtt/után), speed (lejátszási sebesség) és timeOffset (eltolás az idővonalon).
CACurrentMediaTime() — függvény, amely az abszolút időt másodpercben adja vissza az eszköz utolsó újraindítása óta. Alapvető időbélyegként használjuk több animáció szinkronizálásához: állítsa be a beginTime = CACurrentMediaTime() + offset értéket, hogy egy animációs csoportot pontos késleltetéssel indítson el. A CAMediaTiming támogatja a timeline hierarchiát is — a szülő réteg a speed tulajdonságon keresztül gyorsíthatja/lassíthatja az összes gyermek animációt. A UIKit-ben az UIViewPropertyAnimator (iOS 10+) a duration, delay tulajdonságok és a startAnimation/pauseAnimation/stopAnimation visszahívások segítségével absztrahálja az idővonalat. A SwiftUI-ben az Animation struktúra duration-t biztosít az animáció specifikáció részeként, de az idővonal közvetlen kezelése rejtett — egyéni timeline-hoz használja a TimelineView-t (iOS 15+) saját scheduler-rel.
Android-ban az idővonal kezelése az Animator osztályban és leszármazottaiban (ValueAnimator, ObjectAnimator) központosul. A timeline fő tulajdonságai: duration (időtartam milliszekundumban), startDelay (késleltetés az indítás előtt), repeatCount (ismétlések száma), repeatMode (RESTART vagy REVERSE). A setCurrentPlayTime(long) metódus lehetővé teszi az idővonalon való mozgást tetszőleges pozícióba.
A ValueAnimator absztrakciós szintet ad hozzá: duration alapján 0 és 1 közötti értékeket (normalized time) generál, és továbbítja azokat a TimeInterpolator-nak. Az AnimatorSet lehetővé teszi több Animator egyetlen idővonalba történő egyesítését szekvenciális (playSequentially) vagy párhuzamos (playTogether) végrehajtással. A Jetpack Compose-ban az idővonalat az AnimationSpec kezeli: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) és keyframes (pontok az idővonalon egyéni easing-gel minden szegmenshez). A Compose AnimatedContent a transitionSpec számára használja a timeline-t — a fejlesztő leírja, hogyan jelenik meg/tűnik el a tartalom időbeli eltolásokkal. Az Android Performance szerint az animációs idővonalnak a 16 ms (vsync ciklus) többszörösének kell lennie 60fps esetén — a helytelen időtartam kockavesztést okozhat a képernyő frissítési sebességével való összeférhetetlenség miatt.
Az idővonal különösen fontossá válik több animáció kompozíciójakor. Három alapminta: kaszkád — az animációk szekvenciálisan indulnak eltolással (minden következő az előző utáni offset-tel kezdődik), párhuzamos — az összes animáció egyidejűleg indul azonos timeline-lal, és stagger — az animációk átfedéssel indulnak (a második az első befejeződése előtt kezdődik).
iOS-ben a kaszkád minden CAAnimation beginTime = előző beginTime + előző duration segítségével valósul meg. A staggerhez CAAnimationGroup-ot használunk különböző beginTime-értékekkel az egymásba ágyazott animációkban. Android-ban az AnimatorSet támogatja a playSequentially és playTogether funkciókat; a stagger az egyes Animator-ok startDelay beállításával jön létre. A Compose-ban az AnimatedVisibility az enter/exitTransition segítségével a staggerChildren változatban használja a stagger-t a lista elemeinek kaszkádszerű megjelenéséhez. A WWDC és Google I/O 2024 eseményeken mindkét platform bemutatta a timeline vizualizációs eszközeiket: Xcode Animations Inspector és Android Studio Animations Timeline — ezek valós időben mutatják a tényleges idővonalat és a kihagyott kockákat.
Példa egyéni timeline-nal rendelkező animációra: 0,3 másodperc késleltetés, 0,6 másodperc időtartam, autoreverse és végtelen ismétlés. A CAMediaTiming az összes időparamétert kezeli.
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 ms késleltetést ad. autoreverses = true hatására az animáció minden ciklus után ellentétes irányba megy. A repeatCount = .infinity végtelen ciklust hoz létre a „lélegzés” effektushoz. speed = 1,0 — normál sebesség; gyorsítsa az animációt 2-szeresen a 2,0 értékkel.
A SwiftUI TimelineView (iOS 15+) hozzáférést biztosít a rendszer timeline-jához egyéni animációhoz. A nézet a scheduler minden tikénél frissül.
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 a .periodic scheduler-rel 0,1 másodpercenként generál tikket. A zárójelben a context.date — az aktuális idő, amely szinuszos fázissá alakul. A kör mérete 1 Hz frekvenciával pulzál. A TimelineView az egyetlen mód a SwiftUI-ban animáció létrehozására saját idővonallal, Animation API nélkül.
ValueAnimator teljes idővonal-kezeléssel: késleltetés, időtartam, ismétlés. A setCurrentPlayTime metódus lehetővé teszi az ugrást az idővonalon.
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 — késleltetés az indítás előtt. repeatMode = REVERSE visszaállítja az animációt a kezdeti értékre. Az animatedFraction 0 és 1 közötti előrehaladást ad, figyelembe véve a repeatMode-ot. Az addUpdateListener minden kockánál meghívódik (általában 16 ms). A szüneteltetéshez használja a pause() / resume() függvényeket.
Az AnimatorSet lehetővé teszi összetett idővonalak létrehozását több animációból. A példában — szekvenciális végrehajtás átfedéssel (stagger).
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 az összes animációt párhuzamosan indítja, de mindegyiknek saját startDelay-je van — ez hozza létre a stagger hatást: a fadeIn indul először (0 ms), a slideUp 100 ms után, a scale 200 ms után. Az eredmény idővonal: 0 — 100 — 200 — 600 ms. Szekvenciális végrehajtáshoz használja a playSequentially-t.
Gyakran Ismételt Kérdések
Az Animator — absztrakt alaposztály az összes animációhoz. A ValueAnimator — az alosztálya, amely számértékeket animál objektumhoz való kötöttség nélkül. Az ObjectAnimator (a ValueAnimator örököse) egy objektum konkrét tulajdonságát animálja (pl. alpha vagy translationY). Timeline-hoz használja a ValueAnimator-t univerzális időzítőként addUpdateListener visszahívással.
Használja a rendszerórákat (CACurrentMediaTime iOS-ben, System.nanoTime Android-ban) a haladás kiszámításához, ne a kockák számát. Hálózati szinkronizáláshoz használjon NTP-alapú időzítőt offset korrekcióval. Az iOS támogatja az AVAudioSession-t az audio-video timeline milliszekundum pontosságú szinkronizálásához. Kerülje a kockánkénti szinkronizálást — a kockasebesség 30 és 120 fps között változik.
startOffset — késleltetés az animáció kezdete előtt milliszekundumban. Kaszkád és stagger effektusokhoz használjuk. repeatCount — ismétlések száma: 0 = nincs ismétlés, ValueAnimator.INFINITE = végtelen ciklus. A repeatMode határozza meg az irányt: RESTART (visszaállítás a kezdetre) vagy REVERSE (visszirányú mozgás). iOS-ben a startOffset neve beginTime (CACurrentMediaTime-val kombinálva), a repeatCount — a CABasicAnimation azonos nevű tulajdonsága.
A Material Design 200–350 ms-ot ajánl interfész-animációkhoz. A 100 ms-nál rövidebb animációk nem érzékelhetők animációként, az 500 ms-nál hosszabbak — irritálnak. Az elemek be/kilépéséhez: 200–300 ms. A képernyők közötti átmenetekhez: 300–400 ms. Használjon rövidebb időtartamokat kis elemekhez (100–200 ms) és hosszabbakat nagy elemekhez (400–500 ms).
A rángás (jank) a következők miatt fordul elő: hosszú műveletek a fő szálban (blokkolják a renderelést), összetett View/Layer hierarchia overdraw-val, vagy a timeline helytelen használata (az időtartam nem többszöröse a vsync-nek — 16ms 60fps-hez, 8ms 120fps-hez). Használja az Xcode Instruments Animations vagy Android Studio Profiler eszközt a kihagyott kockák észleléséhez. Compose-ban alkalmazza a Modifier.drawWithCache-t az összetett rajzok gyorsítótárazásához.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is