Spring Animation — техника анимације која моделира физичко понашање опруге: маса, крутост (stiffness), пригушење (damping) и почетна брзина. За разлику од линеарних интерполација и Безијеових криви, анимација опруге ствара природно кретање са ефектом пригушених осцилација које изгледа органски на сваком уређају. Према Material Design Guidelines (2026), Spring Animation се користи у 73% анимираних интерфејса топ 100 апликација App Store-а. Више о другим врстама анимације прочитајте у општем водичу за анимацију.
Главне тачке
Spring Animation — је анимација чије кретање описује једначина хармонијског осцилатора са пригушењем. Уместо да интерполира вредност од 0 до 1 у фиксном времену, Spring Animation израчунава положај објекта у сваком тренутку на основу физичких параметара: маса (mass), крутост (stiffness), пригушење (damping) и почетна брзина (initialVelocity). Резултат — природно, органско кретање које не изгледа „механички“.
За разлику од Easing кривих (easeIn, easeOut, cubic-bezier), где је трајање фиксно и путања унапред одређена, Spring Animation се динамички прилагођава: ако промените циљну вредност током анимације, опруга нежно прерачунава путању од тренутне позиције. Ово је посебно важно за анимације које могу бити прекинуте — drag-to-dismiss, pull-to-refresh, картице за превлачење (swipeable cards).
Према Apple WWDC 2025, коришћење анимације опруге уместо фиксних кривих смањује перципирано кашњење (perceived latency) за 30–50% захваљујући природном убрзању на почетку и глатком успоравању на крају. Spring Animation се препоручује за све анимације везане за кретање: померање, скалирање, појављивање/нестајање.
Понашање опруге одређују четири параметра. Stiffness (крутост) — сила повратка у равнотежу: вредност 100–300 даје меку опругу за листе, 500–1000 — тврду за дугмад и прекидаче. Damping (пригушење) — отпор кретању: 0 — бесконачне осцилације, 0.1–0.5 — приметне пригушене осцилације (ефекат одбијања), 1 — критично пригушење (без осцилација, глатко кочење).
| Параметар | Опсег | Ефекат | Пример употребе |
|---|---|---|---|
| Stiffness | 100–1000 | Брзина повратка у равнотежу | 200 = листа, 600 = дугме |
| Damping | 0.0–1.0 | Пригушење осцилација | 0.3 = одбијање, 1.0 = без осцилација |
| Mass | 0.1–10.0 | Инерција објекта | 1.0 = стандард, 3.0 = тежак елемент |
| Initial Velocity | Било који | Почетна брзина у пикселима/с | Брзина прста при drag-to-dismiss |
Критично пригушење (damping = 1.0) — оптимални режим за већину UI анимација: објекат се враћа на циљну позицију максимално брзо без прелета. За ефекат одбијања (нпр. pull-to-refresh) користите damping = 0.3–0.5. За појављивање модалних прозора — damping = 0.6–0.8 са stiffness = 300–500. Маса се ретко мења — стандардна вредност 1.0 одговара скоро свим случајевима.
На iOS-у Spring Animation је доступна кроз UIView.animate(withDuration:delay:usingSpringWithDamping:initialSpringVelocity:) од iOS 7 и CASpringAnimation (Core Animation) од iOS 9. UIView верзија је једноставнија и популарнија за анимацију својстава View-а (transform, alpha, center). CASpringAnimation је прецизнија и пружа приступ stiffness, damping и mass као засебним својствима.
import UIKit
// UIView.animate са параметрима опруге
let originalCenter = view.center
UIView.animate(
withDuration: 0.8,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.3,
options: [.curveEaseInOut, .allowUserInteraction]
) {
view.center = CGPoint(x: originalCenter.x + 100, y: originalCenter.y)
view.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
} completion: { _ in
UIView.animate(withDuration: 0.3) {
view.transform = .identity
}
}
// CASpringAnimation — прецизна контрола параметара
let spring = CASpringAnimation()
spring.keyPath = "transform.scale"
spring.fromValue = 1.0
spring.toValue = 1.5
spring.stiffness = 300
spring.damping = 10
spring.mass = 1.0
spring.initialVelocity = 5.0
"> CASpringAnimation.settlingDuration — вычисленная длительность
spring.duration = spring.settlingDuration
view.layer.add(spring, forKey: "scaleSpring")
CASpringAnimation.settlingDuration — јединствено својство Core Animation-а које израчунава минимално трајање анимације потребно опрузи да достигне равнотежу. То значи да не морате ручно да бирате duration — систем сам одређује колико ће времена трајати пригушење осцилација. За UIView.animate трајање се задаје експлицитно, а damping 0.3–0.7 даје приметан ефекат одбијања. За повезивање са UIPanGestureRecognizer-ом проследите velocityRecognizer.velocity(in: view) као initialSpringVelocity — тако анимација наставља кретање прста истом брзином.
На Android-у SpringAnimation је део библиотеке AndroidX Dynamic Animation (androidx.dynamicanimation). Главна класа — SpringAnimation, која анимира било које float својство View-а или прилагођеног објекта. Параметри се постављају кроз SpringForce — stiffness (подразумевано STIFFNESS_MEDIUM = 600) и dampingRatio (подразумевано DAMPING_RATIO_MEDIUM_BOUNCY = 0.5).
// build.gradle.kts
implementation("androidx.dynamicanimation:dynamicanimation:1.1.0")
// Kotlin: SpringAnimation са прилагођеним SpringForce-ом
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import androidx.dynamicanimation.animation.FloatPropertyCompat
val springAnim = SpringAnimation(myView, SpringAnimation.TRANSLATION_X).apply {
spring = SpringForce().apply {
stiffness = SpringForce.STIFFNESS_LOW // 200.0f
dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY // 0.2f
finalPosition = 300f
}
start()
}
// SpringAnimation са почетном брзином из геста
myView.setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
springAnim.cancel()
false
}
MotionEvent.ACTION_UP -> {
val velocity = velocityTracker?.getXVelocity() ?: 0f
SpringAnimation(myView, SpringAnimation.TRANSLATION_X).apply {
spring = SpringForce(0f) "> Возврат в исходную позицию
setStartVelocity(velocity)
start()
}
true
}
else -> false
}
}
"> SpringAnimation для кастомного свойства
val customAnim = SpringAnimation(
myObject,
object : FloatPropertyCompat<MyClass>("progress") {
override fun getValue(obj: MyClass): Float = obj.progress
override fun setValue(obj: MyClass, value: Float) { obj.progress = value }
}
).apply { start() }
Константе SpringForce-а: STIFFNESS_LOW (200), STIFFNESS_MEDIUM (600), STIFFNESS_HIGH (1800); DAMPING_RATIO_HIGH_BOUNCY (0.2), DAMPING_RATIO_MEDIUM_BOUNCY (0.5), DAMPING_RATIO_LOW_BOUNCY (0.75), DAMPING_RATIO_NO_BOUNCY (1.0). За RecyclerView са анимацијом елемената користите SpringAnimation са DynamicAnimation.OnAnimationEndListener за ослобађање ресурса. За анимацију више својстава користите SpringAnimation.createFor(view, property).
Jetpack Compose: од Compose 1.0, анимација опруге је доступна кроз Animatable са spring() — animationSpec = spring(dampingRatio = 0.5f, stiffness = 300f). Ово је пожељан начин за Compose пројекте, јер SpringAnimation из DynamicAnimation-а је компатибилан само са View системом. Compose верзија spring користи исти физички мотор, али ради у контексту Compose Animation System-а, аутоматски се заустављајући при detachment-у.
Spring Animation и Easing криве (easeInOut, FastOutSlowIn, decelerate) решавају различите задатке. Easing криве — математички задата путања са фиксним временом. Spring — физичка симулација, где време зависи од параметара и почетних услова. За једноставне анимације појављивања/нестајања Easing криве су довољне. За анимације везане за кретање корисника (drag, swipe, scroll), Spring Animation даје природнији одзив.
| Сценарио | Препорука | Параметри |
|---|---|---|
| Појава елемента | Spring (damping = 0.7, stiffness = 400) | Глатка појава са лаганим прелетом |
| Swipe-to-dismiss | Spring + почетна брзина | setStartVelocity(velocity), damping = 0.5 |
| Анимација иконе | Spring (damping = 0.3, stiffness = 600) | Приметно одбијање за акцентне ефекте |
| Scroll физика | Spring (системска) | Не прилагођавати — Android сам управља |
| Промена величине | Easing (FastOutSlowIn) | Easing је поузданији за промене layout-а |
Anti-patterns: не користите Spring Animation за анимацију layout параметара (ширина/висина View-а кроз LayoutParams) — то изазива прерачунавање хијерархије у сваком кадру, што доводи до трзања и пада FPS-а. За анимацију боје такође су боље Easing криве. За анимацију scroll позиције користите системски Scroller са fling-ом — Spring Animation овде не даје предности. За максималне перформансе на Android-у ограничите број истовремено активних SpringAnimation-а на 10–15.
Често постављана питања
Stiffness — крутост опруге (сила повратка). Висока вредност (1000) — брзо, нагло кретање. Ниска (100) — меко, споро. Damping — пригушење (отпор). Ниско (0.2) — дуге осцилације са одбијањем. Високо (1.0) — без осцилација, глатко кочење. Stiffness одређује брзину, damping — присуство и интензитет осцилација.
Почните са damping = 0.5 и stiffness = 400 — ово је универзални стартни сет за iOS и Android. За појављивање елемената повећајте damping на 0.7. За акцентне ефекте (лајк, додавање у корпу) смањите damping на 0.3 са stiffness = 600. За Material Motion користите damping = 0.6 и stiffness = 300. Проверавајте анимацију на реалном уређају, а не у симулатору.
Да, у SwiftUI-ју анимација опруге се поставља кроз .animation(.spring(dampingFraction: 0.5, response: 0.5), value: state) или .spring(blendDuration: 0.3) у withAnimation-у. response и dampingFraction одговарају stiffness и damping из UIKit-а. За interactiveSpring() користите .interactiveSpring() — специјална верзија са почетном брзином из геста, оптимизована за drag анимације.
Проверите SpringForce.finalPosition — ако није постављен или је једнак тренутној вредности, анимација можда неће почети. Позивајте springAnim.cancel() у onPause()-у Activity/Fragment-а. За SpringAnimation у RecyclerView-у користите view.setOnDetachListener { animator.cancel() }. За Jetpack Compose SpringAnimation се аутоматски зауставља при композицији.
Да. На iOS-у позовите view.layer.removeAnimation(forKey:) — анимација ће се зауставити на тренутном кадру. На Android-у позовите springAnim.cancel() — објекат остаје на тренутној позицији. За глатки прелаз на нову циљну вредност ажурирајте finalPosition и не позивајте cancel — SpringAnimation ће аутоматски прерачунати путању. За SkipToEnd користите springAnim.skipToEnd() на Android-у.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође