Главно
Timeline — модел времена анимације који одређује када анимација почиње, колико дуго траје и како се проток времена претвара у промену анимираног параметра. За разлику од анимације кадар по кадар (frame-by-frame), где се сваки кадар поставља ручно, timeline анимација се описује параметрима: duration — укупно трајање, startDelay — кашњење пре почетка, repeatCount — број понављања и timingFunction — крива брзине.
Временска линија ради у нормализованом простору [0, 1] — local time или progress. Нулта вредност одговара почетку анимације, јединица — завршетку. Easing функција претвара линеарни local time у стварни напредак анимације. Ако је repeatCount > 1, timeline се циклично ресетује (RESTART) или инвертује (REVERSE). У iOS-у, CAMediaTiming додаје параметре speed (брзина репродукције, >1 убрзава) и timeOffset (померај временске линије). У Android-у, Animator користи setCurrentPlayTime() за директно управљање позицијом на временској линији. Према Material Design-у, анимације краће од 100 ms се не перципирају као анимација, а дуже од 500 ms — почињу да иритирају. Оптимално трајање за интерфејс анимације је 200–350 ms.
У iOS-у, управљање временском линијом анимације се врши кроз протокол CAMediaTiming, који имплементирају CAAnimation и CALayer. Он пружа својства: duration (трајање у секундама), beginTime (време почетка у односу на родитељски timeline), repeatCount (број понављања), repeatDuration (укупно трајање понављања), autoreverses (аутоматски обрнути правац), fillMode (понашање пре/после анимације), speed (брзина репродукције) и timeOffset (померај на линији).
CACurrentMediaTime() — функција која враћа апсолутно време у секундама од последњег рестартовања уређаја. Користи се као основни временски ознака за синхронизацију више анимација: подесите beginTime = CACurrentMediaTime() + offset за покретање групе анимација са прецизним кашњењем. CAMediaTiming такође подржава хијерархију timeline-а — родитељски слој може убрзати/успорити све подређене анимације кроз својство speed. У UIKit-у, UIViewPropertyAnimator (iOS 10+) апстрахује временску линију кроз својства duration, delay и колбеке startAnimation/pauseAnimation/stopAnimation. У SwiftUI-ју, структура Animation пружа duration као део спецификације анимације, али директно управљање временском линијом је скривено — за прилагођени timeline користите TimelineView (iOS 15+) са сопственим scheduler-ом.
У Android-у, управљање временском линијом је централизовано у класи Animator и њеним наследницима (ValueAnimator, ObjectAnimator). Основна својства timeline-а: duration (трајање у милисекундама), startDelay (кашњење пре почетка), repeatCount (број понављања), repeatMode (RESTART или REVERSE). Метод setCurrentPlayTime(long) омогућава кретање по временској линији на произвољну позицију.
ValueAnimator додаје ниво апстракције: генерише вредности од 0 до 1 (normalized time) на основу duration и прослеђује их TimeInterpolator-у. AnimatorSet омогућава комбиновање више Animator-а у једну временску линију са секвенцијалним (playSequentially) или паралелним (playTogether) извршавањем. У Jetpack Compose-у, временска линија се управља кроз AnimationSpec: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) и keyframes (тачке на временској линији са прилагођеним easing-ом за сваки сегмент). Compose AnimatedContent користи timeline за transitionSpec — програмер описује како садржај постаје/нестаје са временским померајима. Према Android Performance-у, временска линија анимације треба да буде умножак од 16 ms (vsync циклус) за 60fps — неправилно трајање може изазвати прескакање кадрова због неслагања са стопом освежавања екрана.
Временска линија постаје посебно важна при композицији више анимација. Три основна обрасца: каскада — анимације се покрећу секвенцијално са померајем (свака следећа почиње након offset-а од претходне), паралела — све анимације се покрећу истовремено са истим timeline-ом и 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, обе платформе су представиле алате за визуелизацију timeline-а: Xcode Animations Inspector и Android Studio Animations Timeline — они приказују стварну временску линију и прескочене кадрове у реалном времену.
Пример анимације са прилагођеним timeline-ом: кашњење 0.3 сек, трајање 0.6 сек, autoreverse и бесконачно понављање. CAMediaTiming управља свим временским параметрима.
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. autoreverses = true узрокује да анимација иде у супротном смеру након сваког циклуса. repeatCount = .infinity ствара бесконачну петљу ефекта "дисања". speed = 1.0 — нормална брзина; убрзајте анимацију 2 пута вредношћу 2.0.
SwiftUI TimelineView (iOS 15+) пружа приступ системском timeline-у за прилагођену анимацију. Поглед се ажурира при сваком откуцају scheduler-а.
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 Hz. TimelineView је једини начин да се креира анимација са сопственом временском линијом у SwiftUI-ју без Animation API.
ValueAnimator са комплетним управљањем временском линијом: кашњење, трајање, понављање. Метод setCurrentPlayTime омогућава скакање по временској линији.
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 — кашњење пре почетка. repeatMode = REVERSE враћа анимацију на почетну вредност. animatedFraction даје напредак од 0 до 1 узимајући у обзир repeatMode. addUpdateListener се покреће сваки кадар (обично 16 ms). За паузирање користите pause() / resume().
AnimatorSet омогућава креирање сложених временских линија од више анимација. У примеру — секвенцијално извршавање са преклапањем (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 покреће све анимације паралелно, али свака има свој startDelay — ово ствара stagger ефекат: fadeIn почиње први (0 ms), slideUp након 100 ms, scale након 200 ms. Резултујућа временска линија: 0 — 100 — 200 — 600 ms. За секвенцијално извршавање користите playSequentially.
Често постављана питања
Animator — апстрактна базна класа за све анимације. ValueAnimator — њена подкласа која анимира нумеричке вредности без везивања за објекат. ObjectAnimator (наследник ValueAnimator) анимира одређено својство објекта (нпр. alpha или translationY). За timeline користите ValueAnimator као универзални тајмер са колбеком кроз addUpdateListener.
Користите системске сатове (CACurrentMediaTime у iOS-у, System.nanoTime у Android-у) за израчунавање напретка, а не број кадрова. За мрежну синхронизацију примените тајмер заснован на NTP-у са корекцијом offset. iOS подржава AVAudioSession за синхронизацију аудио-видео timeline-а са прецизношћу до милисекунди. Избегавајте синхронизацију по кадровима — брзина кадрова варира од 30 до 120 fps.
startOffset — кашњење пре почетка анимације у милисекундама. Користи се за каскаду и stagger ефекте. repeatCount — број понављања: 0 = без понављања, ValueAnimator.INFINITE = бесконачна петља. repeatMode одређује смер: RESTART (ресетовање на почетак) или REVERSE (обрнути смер). У iOS-у, startOffset се назива beginTime (у комбинацији са CACurrentMediaTime), repeatCount — истоимено својство CABasicAnimation.
Material Design препоручује 200–350 ms за интерфејс анимације. Анимације краће од 100 ms се не перципирају као анимација, дуже од 500 ms — иритирају. За улаз/излаз елемената: 200–300 ms. За транзиције између екрана: 300–400 ms. Користите краћа трајања за мале елементе (100–200 ms) и дужа за велике (400–500 ms).
Трзање (jank) настаје због: дуготрајних операција у главној нити (блокирају рендеровање), сложене хијерархије View/Layer са overdraw, или неправилног коришћења timeline-а (трајање није умножак vsync-а — 16ms за 60fps, 8ms за 120fps). Користите Xcode Instruments Animations или Android Studio Profiler за откривање прескочених кадрова. У Compose-у примените Modifier.drawWithCache за кеширање сложених цртежа.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође