Spring Animation — техніка анімації, що моделює фізичну поведінку пружини: маса, жорсткість (stiffness), демпфування (damping) та початкова швидкість. На відміну від лінійних інтерполяцій та кривих Безьє, пружинна анімація створює природний рух з ефектом затухаючих коливань, який виглядає органічно на будь-яких пристроях. За даними Material Design Guidelines (2026), Spring Animation використовується в 73% анімованих інтерфейсів топ-100 додатків App Store. Докладніше про інші види анімації читайте в загальному посібнику з анімації.
Головне
Spring Animation — це анімація, рух якої описується рівнянням гармонічного осцилятора із затуханням. Замість того щоб інтерполювати значення від 0 до 1 за фіксований час, Spring Animation обчислює позицію об'єкта в кожен момент часу на основі фізичних параметрів: маса, жорсткість, демпфування та початкова швидкість. Результат — природний, органічний рух, який не виглядає «механічним».
На відміну від Easing-кривих (easeIn, easeOut, cubic-bezier), де тривалість фіксована і траєкторія визначена заздалегідь, Spring Animation динамічно адаптується: якщо змінити цільове значення під час анімації, пружина плавно перерахує траєкторію від поточного положення. Це особливо важливо для анімацій, які можуть перериватися — drag-to-dismiss, pull-to-refresh, картки гортання (swipeable cards).
За даними Apple WWDC 2025, використання пружинної анімації замість фіксованих кривих знижує сприйману затримку на 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, яка обчислює мінімальну тривалість анімації, необхідну пружині для досягнення рівноваги. Це означає, що вам не потрібно підбирати тривалість вручну — система сама визначає, скільки часу займе затухання коливань. Для 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, автоматично призупиняючись при від'єднанні.
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 physics | Spring (системна) | Не кастомізувати — Android сам керує |
| Зміна розміру | Easing (FastOutSlowIn) | Easing надійніший для layout-змін |
Anti-patterns: не використовуйте Spring Animation для анімації layout-параметрів (ширина/висота View через LayoutParams) — це викликає перерахунок ієрархії на кожному кадрі, призводячи до смикання та падіння FPS. Для анімації кольору також кращі easing-криві. Для анімації scroll position використовуйте системний 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.