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 вычисляет позицию объекта в каждый момент времени на основе физических параметров: масса (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 рекомендуется для любых анимаций, связанных с движением: перемещение, масштабирование, появление/исчезновение.

Физические параметры пружины: 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, которое вычисляет минимальную длительность анимации, необходимую пружине для достижения равновесия. Это значит, что вам не нужно подбирать duration вручную — система сама определяет, сколько времени займёт затухание колебаний. Для 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, автоматически приостанавливаясь при detachment.

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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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