Belangrijkste
Timeline — is een tijdsmodel van animatie dat bepaalt wanneer een animatie begint, hoe lang deze duurt en hoe het verloop van de tijd wordt omgezet in een verandering van de geanimeerde parameter. In tegenstelling tot frame-voor-frame animatie (frame-by-frame), waarbij elk frame handmatig wordt ingesteld, wordt timeline-animatie beschreven met parameters: duration — totale duur, startDelay — vertraging voor start, repeatCount — aantal herhalingen, en timingFunction — snelheidscurve.
De tijdlijn werkt in een genormaliseerde ruimte [0, 1] — local time of progress. De waarde nul komt overeen met het begin van de animatie, één met het einde. De easing-functie zet lineaire local time om in werkelijke animatievoortgang. Als repeatCount > 1 is, wordt de timeline cyclisch gereset (RESTART) of omgekeerd (REVERSE). In iOS voegt CAMediaTiming parameters speed (afspeelsnelheid, >1 versnelt) en timeOffset (verschuiving van de tijdlijn) toe. In Android gebruikt Animator setCurrentPlayTime() voor direct beheer van de positie op de tijdlijn. Volgens Material Design worden animaties korter dan 100 ms niet als animatie waargenomen, en langer dan 500 ms — beginnen te irriteren. De optimale duur voor interface-animaties is 200–350 ms.
In iOS wordt het beheer van de animatie-tijdlijn uitgevoerd via het protocol CAMediaTiming, dat wordt geïmplementeerd door CAAnimation en CALayer. Het biedt de eigenschappen: duration (duur in seconden), beginTime (starttijd ten opzichte van de bovenliggende timeline), repeatCount (aantal herhalingen), repeatDuration (totale duur van herhalingen), autoreverses (automatische omgekeerde beweging), fillMode (gedrag voor/na animatie), speed (afspeelsnelheid) en timeOffset (verschuiving op de as).
CACurrentMediaTime() — functie die de absolute tijd in seconden teruggeeft sinds de laatste herstart van het apparaat. Gebruikt als basistijdstempel voor synchronisatie van meerdere animaties: stel beginTime = CACurrentMediaTime() + offset in om een groep animaties met exacte vertraging te starten. CAMediaTiming ondersteunt ook de hiërarchie van timeline — de bovenliggende laag kan alle onderliggende animaties versnellen/vertragen via de eigenschap speed. In UIKit abstraheert UIViewPropertyAnimator (iOS 10+) de tijdlijn via de eigenschappen duration, delay en callbacks startAnimation/pauseAnimation/stopAnimation. In SwiftUI biedt de structuur Animation duration als onderdeel van de animatiespecificatie, maar direct beheer van de tijdlijn is verborgen — voor een aangepaste timeline gebruikt u TimelineView (iOS 15+) met uw eigen scheduler.
In Android is het beheer van de tijdlijn gecentraliseerd in de klasse Animator en zijn subklassen (ValueAnimator, ObjectAnimator). De belangrijkste eigenschappen van de timeline: duration (duur in milliseconden), startDelay (vertraging voor start), repeatCount (aantal herhalingen), repeatMode (RESTART of REVERSE). De methode setCurrentPlayTime(long) maakt het mogelijk om naar elke gewenste positie op de tijdlijn te gaan.
ValueAnimator voegt een abstractielaag toe: het genereert waarden van 0 tot 1 (normalized time) op basis van duration en geeft ze door aan TimeInterpolator. AnimatorSet maakt het mogelijk om meerdere Animator-objecten te combineren in één tijdlijn met sequentiële (playSequentially) of parallelle (playTogether) uitvoering. In Jetpack Compose wordt de tijdlijn beheerd via AnimationSpec: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) en keyframes (punten op de tijdlijn met aangepaste easing voor elk segment). Compose AnimatedContent gebruikt de timeline voor transitionSpec — de ontwikkelaar beschrijft hoe inhoud verschijnt/verdwijnt met tijdsverschuivingen. Volgens Android Performance moet de animatie-tijdlijn een veelvoud zijn van 16 ms (vsync-cyclus) voor 60fps — een onjuiste duur kan frameverlies veroorzaken door incompatibiliteit met de verversingssnelheid van het scherm.
De tijdlijn wordt vooral belangrijk bij de compositie van meerdere animaties. Drie basispatronen: cascade — animaties worden sequentieel gestart met een verschuiving (elke volgende begint na een offset van de vorige), parallel — alle animaties worden gelijktijdig gestart met dezelfde timeline, en stagger — animaties worden met overlapping gestart (de tweede begint voordat de eerste is afgelopen).
In iOS wordt de cascade geïmplementeerd via beginTime van elke CAAnimation = vorige beginTime + vorige duration. Voor stagger wordt CAAnimationGroup gebruikt met verschillende beginTime in geneste animaties. In Android ondersteunt AnimatorSet playSequentially en playTogether; stagger wordt gecreëerd door startDelay in te stellen voor elke Animator. In Compose gebruikt AnimatedVisibility met enter/exitTransition stagger in de variant staggerChildren voor het trapsgewijs verschijnen van lijstelementen. Op WWDC en Google I/O 2024 presenteerden beide platforms tools voor timeline-visualisatie: Xcode Animations Inspector en Android Studio Animations Timeline — ze tonen de werkelijke tijdlijn en overgeslagen frames in realtime.
Voorbeeld van een animatie met aangepaste timeline: vertraging 0,3 sec, duur 0,6 sec, autoreverse en oneindige herhaling. CAMediaTiming beheert alle tijdelijke parameters.
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 geeft een vertraging van 300 ms. autoreverses = true zorgt dat de animatie na elke cyclus in tegengestelde richting gaat. repeatCount = .infinity creëert een oneindige lus van het “adembalend” effect. speed = 1,0 — normale snelheid; versnel de animatie 2 keer met waarde 2,0.
SwiftUI TimelineView (iOS 15+) biedt toegang tot de systeem-timeline voor aangepaste animatie. De weergave wordt bij elke tik van de scheduler bijgewerkt.
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 met .periodic scheduler genereert ticks elke 0,1 sec. In de closure, context.date — de huidige tijd die wordt omgezet in de fase van een sinusoïde. De grootte van de cirkel pulseert met een frequentie van 1 Hz. TimelineView is de enige manier om een animatie met een eigen tijdlijn te maken in SwiftUI zonder Animation API.
ValueAnimator met volledig beheer van de tijdlijn: vertraging, duur, herhaling. De methode setCurrentPlayTime maakt springen op de tijdlijn mogelijk.
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 — vertraging voor start. repeatMode = REVERSE brengt de animatie terug naar de beginwaarde. animatedFraction geeft de voortgang van 0 tot 1, rekening houdend met repeatMode. addUpdateListener wordt elk frame aangeroepen (meestal 16 ms). Gebruik pause() / resume() om te pauzeren.
AnimatorSet maakt het mogelijk om complexe tijdlijnen te maken van meerdere animaties. In het voorbeeld — sequentiële uitvoering met overlapping (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 start alle animaties parallel, maar elke heeft zijn eigen startDelay — dit creëert het staggering-effect: fadeIn start als eerste (0 ms), slideUp na 100 ms, scale na 200 ms. Resulterende tijdlijn: 0 — 100 — 200 — 600 ms. Gebruik playSequentially voor sequentiële uitvoering.
Veelgestelde vragen
Animator — abstracte basisklasse voor alle animaties. ValueAnimator — zijn subklasse die numerieke waarden animeert zonder binding aan een object. ObjectAnimator (inherit van ValueAnimator) animeert een specifieke eigenschap van een object (bijv. alpha of translationY). Gebruik voor timeline ValueAnimator als universele timer met callback via addUpdateListener.
Gebruik systeemklokken (CACurrentMediaTime in iOS, System.nanoTime in Android) voor het berekenen van de voortgang, niet het aantal frames. Gebruik voor netwerksynchronisatie een NTP-gebaseerde timer met offsetcorrectie. iOS ondersteunt AVAudioSession voor synchronisatie van audio-video timeline met millisecondenauwkeurigheid. Vermijd synchronisatie op frames — de framerate varieert van 30 tot 120 fps.
startOffset — vertraging voor het begin van de animatie in milliseconden. Gebruikt voor cascade- en stagger-effecten. repeatCount — aantal herhalingen: 0 = geen herhaling, ValueAnimator.INFINITE = oneindige lus. repeatMode bepaalt de richting: RESTART (reset naar begin) of REVERSE (omgekeerde beweging). In iOS heet startOffset beginTime (in combinatie met CACurrentMediaTime), repeatCount — de gelijknamige eigenschap van CABasicAnimation.
Material Design beveelt 200–350 ms aan voor interface-animaties. Animaties korter dan 100 ms worden niet als animatie waargenomen, langer dan 500 ms — irriteren. Voor in/uit van elementen: 200–300 ms. Voor overgangen tussen schermen: 300–400 ms. Gebruik kortere duren voor kleine elementen (100–200 ms) en langere voor grote (400–500 ms).
Schokkerigheid (jank) ontstaat door: langdurige bewerkingen in de hoofdthread (blokkeren rendering), complexe View/Layer-hiërarchie met overdraw, of onjuist gebruik van de timeline (duur is geen veelvoud van vsync — 16ms voor 60fps, 8ms voor 120fps). Gebruik Xcode Instruments Animations of Android Studio Profiler om overgeslagen frames te detecteren. Gebruik in Compose Modifier.drawWithCache voor caching van complexe tekeningen.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook