Mga pangunahing punto
Timeline — ay ang modelo ng oras ng animasyon na tumutukoy kung kailan magsisimula ang animasyon, gaano ito katagal at kung paano ang pagdaan ng oras ay nababago sa pagbabago ng parameter na ina-animate. Hindi tulad ng frame-by-frame na animasyon, kung saan ang bawat frame ay manu-manong itinatakda, ang timeline animation ay inilalarawan ng mga parameter: duration — kabuuang tagal, startDelay — pagkaantala bago magsimula, repeatCount — bilang ng mga pag-uulit at timingFunction — kurba ng bilis.
Ang timeline ay gumagana sa normalized na espasyo [0, 1] — local time o progress. Ang zero value ay tumutugma sa simula ng animasyon, isa — sa pagkumpleto. Ang easing function ay nagko-convert ng linear local time sa aktwal na progreso ng animasyon. Kung ang repeatCount > 1, ang timeline ay paikot na nire-reset (RESTART) o binabaligtad (REVERSE). Sa iOS, ang CAMediaTiming ay nagdaragdag ng mga parameter na speed (bilis ng pag-playback, >1 nagpapabilis) at timeOffset (paglipat ng timeline). Sa Android, ang Animator ay gumagamit ng setCurrentPlayTime() para sa direktang kontrol ng posisyon sa timeline. Ayon sa Material Design, ang mga animasyon na mas maikli sa 100 ms ay hindi napapansin bilang mga animasyon, at mas mahaba sa 500 ms — nagsisimulang makairita. Ang optimal na tagal para sa mga interface animation ay 200–350 ms.
Sa iOS, ang pamamahala ng timeline ng animasyon ay isinasagawa sa pamamagitan ng protocol na CAMediaTiming, na ipinapatupad ng CAAnimation at CALayer. Nagbibigay ito ng mga property: duration (tagal sa segundo), beginTime (oras ng pagsisimula kaugnay ng parent timeline), repeatCount (bilang ng pag-uulit), repeatDuration (kabuuang tagal ng pag-uulit), autoreverses (awtomatikong pagbaliktad), fillMode (pag-uugali bago/pagkatapos ng animasyon), speed (bilis ng pag-playback) at timeOffset (paglipat sa timeline).
CACurrentMediaTime() — function na nagbabalik ng absolute na oras sa segundo mula noong huling pag-restart ng device. Ginagamit bilang batayang timestamp para sa pag-synchronize ng maraming animasyon: itakda ang beginTime = CACurrentMediaTime() + offset upang ilunsad ang isang grupo ng mga animasyon na may tumpak na pagkaantala. Sinusuportahan din ng CAMediaTiming ang hierarchy ng timeline — ang parent layer ay maaaring bumilis/bagalan ang lahat ng child animation sa pamamagitan ng speed property. Sa UIKit, ang UIViewPropertyAnimator (iOS 10+) ay nag-abstrak ng timeline sa pamamagitan ng duration, delay properties at callbacks na startAnimation/pauseAnimation/stopAnimation. Sa SwiftUI, ang Animation structure ay nagbibigay ng duration bilang bahagi ng specification ng animasyon, ngunit ang direktang kontrol ng timeline ay nakatago — para sa custom na timeline gamitin ang TimelineView (iOS 15+) na may sarili mong scheduler.
Sa Android, ang pamamahala ng timeline ay sentralisado sa klase na Animator at mga subclass nito (ValueAnimator, ObjectAnimator). Ang mga pangunahing property ng timeline: duration (tagal sa millisecond), startDelay (pagkaantala bago magsimula), repeatCount (bilang ng pag-uulit), repeatMode (RESTART o REVERSE). Ang pamamaraang setCurrentPlayTime(long) ay nagpapahintulot ng paggalaw sa kahabaan ng timeline sa anumang posisyon.
Ang ValueAnimator ay nagdaragdag ng antas ng abstraction: bumubuo ng mga halaga mula 0 hanggang 1 (normalized time) batay sa duration at ipinapasa ang mga ito sa TimeInterpolator. Ang AnimatorSet ay nagpapahintulot ng pagsasama-sama ng maraming Animator sa iisang timeline na may sunud-sunod (playSequentially) o parallel (playTogether) na pagpapatupad. Sa Jetpack Compose, ang timeline ay pinamamahalaan sa pamamagitan ng AnimationSpec: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) at keyframes (mga punto sa timeline na may custom na easing para sa bawat segment). Ang Compose AnimatedContent ay gumagamit ng timeline para sa transitionSpec — inilalarawan ng developer kung paano lumilitaw/nawawala ang nilalaman na may mga paglipat ng oras. Ayon sa Android Performance, ang timeline ng animasyon ay dapat na multiple ng 16 ms (vsync cycle) para sa 60fps — ang maling tagal ay maaaring magdulot ng pagkawala ng frame dahil sa hindi pagkakatugma sa refresh rate ng display.
Ang timeline ay nagiging lalong mahalaga sa komposisyon ng maraming animasyon. Tatlong pangunahing pattern: kaskada — ang mga animasyon ay inilulunsad nang sunud-sunod na may paglipat (bawat susunod ay magsisimula pagkatapos ng offset mula sa nauna), parallel — lahat ng animasyon ay inilulunsad nang sabay-sabay na may parehong timeline at stagger — ang mga animasyon ay inilulunsad na may overlap (ang pangalawa ay magsisimula bago matapos ang una).
Sa iOS, ang kaskada ay ipinapatupad sa pamamagitan ng beginTime ng bawat CAAnimation = nakaraang beginTime + nakaraang duration. Para sa stagger, ginagamit ang CAAnimationGroup na may iba't ibang beginTime sa mga nested animation. Sa Android, ang AnimatorSet ay sumusuporta sa playSequentially at playTogether; ang stagger ay nilikha sa pamamagitan ng pagtatakda ng startDelay para sa bawat Animator. Sa Compose, ang AnimatedVisibility na may enter/exitTransition ay gumagamit ng stagger sa variant na staggerChildren para sa cascading na paglitaw ng mga elemento ng listahan. Sa WWDC at Google I/O 2024, ang parehong platform ay nagpakilala ng mga tool sa visualization ng timeline: Xcode Animations Inspector at Android Studio Animations Timeline — ipinapakita nila ang aktwal na timeline at mga nalaglag na frame sa real-time.
Halimbawa ng animasyon na may custom na timeline: pagkaantala 0.3 segundo, tagal 0.6 segundo, autoreverse at walang katapusang pag-uulit. Ang CAMediaTiming ay namamahala sa lahat ng parameter ng oras.
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 ay nagbibigay ng pagkaantala ng 300 ms. autoreverses = true ay nagiging sanhi ng animasyon na tumakbo sa reverse direksyon pagkatapos ng bawat cycle. repeatCount = .infinity ay lumilikha ng walang katapusang loop ng „paghinga" na epekto. speed = 1.0 — normal na bilis; pabilisin ang animasyon ng 2 beses sa halagang 2.0.
Ang SwiftUI TimelineView (iOS 15+) ay nagbibigay ng access sa system timeline para sa custom na animasyon. Ang view ay ina-update sa bawat tick ng 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 na may .periodic scheduler ay bumubuo ng mga tick bawat 0.1 segundo. Sa closure, ang context.date — kasalukuyang oras na na-convert sa phase ng sinusoid. Ang laki ng bilog ay pumipintig na may frequency na 1 Hz. Ang TimelineView ay ang tanging paraan upang lumikha ng animasyon na may sariling timeline sa SwiftUI nang walang Animation API.
ValueAnimator na may buong kontrol ng timeline: pagkaantala, tagal, pag-uulit. Ang pamamaraang setCurrentPlayTime ay nagpapahintulot ng pagtalon sa kahabaan ng timeline.
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 — pagkaantala bago magsimula. repeatMode = REVERSE ay nagbabalik ng animasyon sa paunang halaga. Ang animatedFraction ay nagbibigay ng progreso mula 0 hanggang 1 na isinasaalang-alang ang repeatMode. Ang addUpdateListener ay nagti-trigger bawat frame (karaniwang 16 ms). Para sa pag-pause, gamitin ang pause() / resume().
Ang AnimatorSet ay nagpapahintulot ng paglikha ng mga kumplikadong timeline mula sa maraming animasyon. Sa halimbawa — sunud-sunod na pagpapatupad na may overlap (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 ay nagpapatakbo ng lahat ng animasyon nang parallel, ngunit bawat isa ay may sariling startDelay — ito ay lumilikha ng stagger effect: ang fadeIn ay unang nagsisimula (0 ms), slideUp pagkatapos ng 100 ms, scale pagkatapos ng 200 ms. Panghuling timeline: 0 — 100 — 200 — 600 ms. Para sa sunud-sunod na pagpapatupad, gamitin ang playSequentially.
Mga madalas itanong
Ang Animator — abstract na base class para sa lahat ng animasyon. Ang ValueAnimator ay subclass nito na nag-a-animate ng mga numerikong halaga nang walang pagkakatali sa isang bagay. Ang ObjectAnimator (tagapagmana ng ValueAnimator) ay nag-a-animate ng partikular na property ng isang bagay (hal. alpha o translationY). Para sa timeline, gamitin ang ValueAnimator bilang unibersal na timer na may callback sa pamamagitan ng addUpdateListener.
Gamitin ang mga orasan ng system (CACurrentMediaTime sa iOS, System.nanoTime sa Android) para sa pagkalkula ng progreso, hindi ang bilang ng mga frame. Para sa network synchronization, mag-apply ng NTP-based timer na may correction ng offset. Sinusuportahan ng iOS ang AVAudioSession para sa pag-synchronize ng audio-video timeline na may katumpakan ng millisecond. Iwasan ang pag-synchronize batay sa frame — ang frame rate ay nag-iiba mula 30 hanggang 120 fps.
startOffset — pagkaantala bago magsimula ang animasyon sa millisecond. Ginagamit para sa cascade at stagger effect. repeatCount — bilang ng pag-uulit: 0 = walang pag-uulit, ValueAnimator.INFINITE = walang katapusang loop. Ang repeatMode ay tumutukoy sa direksyon: RESTART (reset sa simula) o REVERSE (reverse na paggalaw). Sa iOS, ang startOffset ay tinatawag na beginTime (sa kombinasyon sa CACurrentMediaTime), repeatCount — ang parehong pangalan na property ng CABasicAnimation.
Inirerekomenda ng Material Design ang 200–350 ms para sa mga interface animation. Ang mga animasyon na mas maikli sa 100 ms ay hindi napapansin bilang mga animasyon, mas mahaba sa 500 ms — nakakairita. Para sa pagpasok/paglabas ng mga elemento: 200–300 ms. Para sa mga transisyon sa pagitan ng mga screen: 300–400 ms. Gumamit ng mas maikling tagal para sa maliliit na elemento (100–200 ms) at mas mahaba para sa malalaki (400–500 ms).
Ang pagkikibot (jank) ay nangyayari dahil sa: mahabang operasyon sa pangunahing thread (humaharang sa rendering), kumplikadong hierarchy ng View/Layer na may overdraw, o maling paggamit ng timeline (tagal ay hindi multiple ng vsync — 16ms para sa 60fps, 8ms para sa 120fps). Gamitin ang Xcode Instruments Animations o Android Studio Profiler para sa pag-detect ng mga nalaglag na frame. Sa Compose, mag-apply ng Modifier.drawWithCache para sa pag-cache ng mga kumplikadong drawing.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din