Spring Animation — фізика пружини в анімаціях

Автор: IT Sectr Опубліковано: 2026-03-01 Час читання: 10 хв

Spring Animation — техніка анімації, що моделює фізичну поведінку пружини: маса, жорсткість (stiffness), демпфування (damping) та початкова швидкість. На відміну від лінійних інтерполяцій та кривих Безьє, пружинна анімація створює природний рух з ефектом затухаючих коливань, який виглядає органічно на будь-яких пристроях. За даними Material Design Guidelines (2026), Spring Animation використовується в 73% анімованих інтерфейсів топ-100 додатків App Store. Докладніше про інші види анімації читайте в загальному посібнику з анімації.

Головне

  • Spring Animation — анімація на основі фізичної моделі пружини з масою та демпфуванням.
  • Damping — коефіцієнт затухання коливань: 0 = нескінченні, 1 = без перельоту.
  • Stiffness — жорсткість пружини: чим вище, тим швидше повернення до рівноваги.
  • CASpringAnimation — клас iOS для пружинної анімації CALayer з підрахунком тривалості.
  • SpringAnimation — клас Android з бібліотеки AndroidX Dynamic Animation.

Що таке Spring Animation?

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 рекомендується для будь-яких анімацій, пов'язаних із рухом: переміщення, масштабування, поява/зникнення.

Фізичні параметри пружини: damping та stiffness

Поведінка пружини визначається чотирма параметрами. Stiffness (жорсткість) — сила повернення до рівноваги: значення 100–300 дає м'яку пружину для списків, 500–1000 — жорстку для кнопок і перемикачів. Damping (демпфування) — опір руху: 0 — нескінченні коливання, 0.1–0.5 — помітні затухаючі коливання (ефект відскоку), 1 — критичне демпфування (без коливань, плавне гальмування).

ПараметрДіапазонЕфектПриклад використання
Stiffness100–1000Швидкість повернення до рівноваги200 = список, 600 = кнопка
Damping0.0–1.0Затухання коливань0.3 = відскок, 1.0 = без коливань
Mass0.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 підходить майже для всіх випадків.

Spring Animation в iOS (Swift)

На 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 як окремих властивостей.

swift
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 — так анімація продовжить рух пальця з тією ж швидкістю.

Spring Animation в Android (Kotlin)

На Android SpringAnimation входить до бібліотеки AndroidX Dynamic Animation (androidx.dynamicanimation). Основний клас — SpringAnimation, який анімує будь-яку float-властивість View або кастомного об'єкта. Параметри задаються через SpringForce — stiffness (за замовчуванням STIFFNESS_MEDIUM = 600) та dampingRatio (за замовчуванням DAMPING_RATIO_MEDIUM_BOUNCY = 0.5).

kotlin
// 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 vs Easing: коли що вибрати

Spring Animation та Easing-криві (easeInOut, FastOutSlowIn, decelerate) вирішують різні завдання. Easing-криві — це математично задана траєкторія з фіксованим часом. Spring — фізична симуляція, де час залежить від параметрів і початкових умов. Для простих анімацій появи/зникнення easing-кривих достатньо. Для анімацій, пов'язаних із рухом користувача (drag, swipe, scroll), Spring Animation дає більш природний відгук.

СценарійРекомендаціяПараметри
Поява елементаSpring (damping = 0.7, stiffness = 400)Плавна поява з легким перельотом
Swipe-to-dismissSpring + початкова швидкістьsetStartVelocity(velocity), damping = 0.5
Анімація іконкиSpring (damping = 0.3, stiffness = 600)Помітний відскок для акцентних ефектів
Scroll physicsSpring (системна)Не кастомізувати — Android сам керує
Зміна розміруEasing (FastOutSlowIn)Easing надійніший для layout-змін

Anti-patterns: не використовуйте Spring Animation для анімації layout-параметрів (ширина/висота View через LayoutParams) — це викликає перерахунок ієрархії на кожному кадрі, призводячи до смикання та падіння FPS. Для анімації кольору також кращі easing-криві. Для анімації scroll position використовуйте системний Scroller з fling — Spring Animation тут не дає переваг. Для максимальної продуктивності на Android обмежте кількість одночасно активних SpringAnimation до 10–15.

Часті запитання

Яка різниця між damping та stiffness?

Stiffness — жорсткість пружини (сила повернення). Високе значення (1000) — швидкий, різкий рух. Низьке (100) — м'який, повільний рух. Damping — демпфування (опір). Низьке (0.2) — довгі коливання з відскоком. Високе (1.0) — без коливань, плавне гальмування. Stiffness визначає швидкість, damping — наявність та інтенсивність коливань.

Як підібрати правильні параметри Spring Animation?

Почніть з damping = 0.5 та stiffness = 400 — це універсальний стартовий набір для iOS та Android. Для появи елементів збільште damping до 0.7. Для акцентних ефектів (лайк, додавання в кошик) зменшіть damping до 0.3 з stiffness = 600. Для Material Motion використовуйте damping = 0.6 та stiffness = 300. Перевіряйте анімацію на реальному пристрої, а не в симуляторі.

Чи працює Spring Animation у SwiftUI?

Так, у SwiftUI пружинна анімація задається через .animation(.spring(dampingFraction: 0.5, response: 0.5), value: state) або .spring(blendDuration: 0.3) у withAnimation. response та dampingFraction повторюють stiffness та damping з UIKit. Для interactiveSpring() використовуйте .interactiveSpring() — спеціальна версія з початковою швидкістю від жесту, оптимізована для drag-анімацій.

Чому Spring Animation на Android не зупиняється?

Перевірте SpringForce.finalPosition — якщо він не встановлений або дорівнює поточному значенню, анімація може не початися. Викликайте springAnim.cancel() в onPause() Activity/Fragment. Для SpringAnimation у RecyclerView використовуйте view.setOnDetachListener { animator.cancel() }. Для Jetpack Compose SpringAnimation автоматично призупиняється при композиції.

Чи можна перервати Spring Animation плавно?

Так. На iOS викличте view.layer.removeAnimation(forKey:) — анімація зупиниться на поточному кадрі. На Android викличте springAnim.cancel() — об'єкт залишиться на поточній позиції. Для плавного переходу до нового цільового значення оновіть finalPosition і не викликайте cancel — SpringAnimation автоматично перерахує траєкторію. Для SkipToEnd використовуйте springAnim.skipToEnd() на Android.

Підсумки

  • Spring Animation — анімація на основі фізичної моделі пружини з параметрами stiffness та damping.
  • Damping — коефіцієнт затухання (0 = нескінченні коливання, 1 = критичне демпфування).
  • Stiffness — жорсткість пружини: 200 (soft), 600 (medium), 1800 (high) на Android.
  • iOS: UIView.animate з usingSpringWithDamping (UIKit) або CASpringAnimation (Core Animation).
  • Android: SpringAnimation + SpringForce з AndroidX Dynamic Animation.
  • Jetpack Compose: animationSpec = spring(dampingRatio, stiffness) через Animatable.
  • Spring Animation кращий за Easing для анімацій з жестами (swipe, drag, pull-to-refresh).

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також