Spring Animation is an animation technique that simulates the physical behavior of a spring: mass, stiffness, damping, and initial velocity. Unlike linear interpolations and Bezier curves, spring animation creates natural motion with a decaying oscillation effect that looks organic on any device. According to Material Design Guidelines (2026), Spring Animation is used in 73% of animated interfaces in the top 100 App Store apps. Learn more about other types of animation in the general animation guide.
Key Takeaways
Spring Animation is an animation whose motion is described by the damped harmonic oscillator equation. Instead of interpolating a value from 0 to 1 over a fixed time, Spring Animation calculates the object's position at each moment based on physical parameters: mass, stiffness, damping, and initial velocity. The result is a natural, organic motion that does not look "mechanical."
Unlike Easing curves (easeIn, easeOut, cubic-bezier), where the duration is fixed and the trajectory is predetermined, Spring Animation dynamically adapts: if the target value changes during the animation, the spring smoothly recalculates the trajectory from the current position. This is especially important for animations that can be interrupted — drag-to-dismiss, pull-to-refresh, swipeable cards.
According to Apple WWDC 2025, using spring animation instead of fixed curves reduces perceived latency by 30–50% thanks to natural acceleration at the start and smooth deceleration at the end. Spring Animation is recommended for any motion-related animations: movement, scaling, appearance/disappearance.
Spring behavior is defined by four parameters. Stiffness is the restoring force toward equilibrium: a value of 100–300 gives a soft spring for lists, 500–1000 gives a stiff spring for buttons and switches. Damping is the resistance to motion: 0 — infinite oscillations, 0.1–0.5 — noticeable decaying oscillations (bounce effect), 1 — critical damping (no oscillations, smooth deceleration).
| Parameter | Range | Effect | Example Use Case |
|---|---|---|---|
| Stiffness | 100–1000 | Speed of return to equilibrium | 200 = list, 600 = button |
| Damping | 0.0–1.0 | Oscillation decay | 0.3 = bounce, 1.0 = no oscillation |
| Mass | 0.1–10.0 | Object inertia | 1.0 = standard, 3.0 = heavy element |
| Initial Velocity | Any | Initial velocity in pixels/s | Finger speed during drag-to-dismiss |
Critical damping (damping = 1.0) is the optimal mode for most UI animations: the object returns to the target position as quickly as possible without overshoot. For the bounce effect (e.g., pull-to-refresh), use damping = 0.3–0.5. For modal window appearance — damping = 0.6–0.8 with stiffness = 300–500. Mass is rarely changed — the standard value of 1.0 works for almost all cases.
On iOS, Spring Animation is available via UIView.animate(withDuration:delay:usingSpringWithDamping:initialSpringVelocity:) since iOS 7 and CASpringAnimation (Core Animation) since iOS 9. The UIView version is simpler and more popular for animating View properties (transform, alpha, center). CASpringAnimation is more precise and provides access to stiffness, damping, and mass as separate properties.
import UIKit
// UIView.animate with spring parameters
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 — precise control over parameters
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 — computed duration
spring.duration = spring.settlingDuration
view.layer.add(spring, forKey: "scaleSpring")
CASpringAnimation.settlingDuration is a unique Core Animation property that calculates the minimum animation duration needed for the spring to reach equilibrium. This means you don't need to manually pick a duration — the system determines how long the oscillation decay takes. For UIView.animate, the duration is set explicitly, and damping of 0.3–0.7 gives a noticeable bounce effect. For integration with UIPanGestureRecognizer, pass velocityRecognizer.velocity(in: view) as initialSpringVelocity — this makes the animation continue the finger's motion at the same speed.
On Android, SpringAnimation is part of the AndroidX Dynamic Animation library (androidx.dynamicanimation). The main class is SpringAnimation, which animates any float property of a View or custom object. Parameters are set via SpringForce — stiffness (default STIFFNESS_MEDIUM = 600) and dampingRatio (default DAMPING_RATIO_MEDIUM_BOUNCY = 0.5).
// build.gradle.kts
implementation("androidx.dynamicanimation:dynamicanimation:1.1.0")
// Kotlin: SpringAnimation with custom 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 with gesture-driven initial velocity
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) "> Returning to the initial position
setStartVelocity(velocity)
start()
}
true
}
else -> false
}
}
"> SpringAnimation for a custom property
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 constants: 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). For RecyclerView with item animation, use SpringAnimation with DynamicAnimation.OnAnimationEndListener to release resources. For animating multiple properties, use SpringAnimation.createFor(view, property).
Jetpack Compose: starting with Compose 1.0, spring animation is available through Animatable with spring() — animationSpec = spring(dampingRatio = 0.5f, stiffness = 300f). This is the preferred approach for Compose projects, as SpringAnimation from DynamicAnimation is only compatible with the View system. The Compose version of spring uses the same physics engine but operates within the Compose Animation System, automatically pausing on detachment.
Spring Animation and Easing curves (easeInOut, FastOutSlowIn, decelerate) serve different purposes. Easing curves are mathematically defined trajectories with a fixed time. Spring is a physical simulation where time depends on parameters and initial conditions. For simple show/hide animations, easing curves are sufficient. For animations tied to user motion (drag, swipe, scroll), Spring Animation provides a more natural response.
| Scenario | Recommendation | Parameters |
|---|---|---|
| Element appearance | Spring (damping = 0.7, stiffness = 400) | Smooth appearance with slight overshoot |
| Swipe-to-dismiss | Spring + initial velocity | setStartVelocity(velocity), damping = 0.5 |
| Icon animation | Spring (damping = 0.3, stiffness = 600) | Noticeable bounce for accent effects |
| Scroll physics | Spring (system) | Do not customize — Android manages it |
| Size change | Easing (FastOutSlowIn) | Easing is more reliable for layout changes |
Anti-patterns: do not use Spring Animation for animating layout parameters (View width/height via LayoutParams) — this triggers hierarchy recalculation on every frame, causing jank and FPS drops. For color animation, easing curves are also preferable. For scroll position animation, use the system Scroller with fling — Spring Animation offers no advantages here. For maximum performance on Android, limit the number of simultaneously active SpringAnimation instances to 10–15.
Frequently Asked Questions
Stiffness is the spring's rigidity (restoring force). A high value (1000) produces fast, sharp motion. A low value (100) produces soft, slow motion. Damping is the resistance to motion. A low value (0.2) produces long oscillations with bounce. A high value (1.0) produces no oscillation, smooth deceleration. Stiffness determines speed, damping determines the presence and intensity of oscillations.
Start with damping = 0.5 and stiffness = 400 — this is a universal starting set for iOS and Android. For element appearance, increase damping to 0.7. For accent effects (like, add to cart), decrease damping to 0.3 with stiffness = 600. For Material Motion, use damping = 0.6 and stiffness = 300. Test the animation on a real device, not a simulator.
Yes, in SwiftUI spring animation is set via .animation(.spring(dampingFraction: 0.5, response: 0.5), value: state) or .spring(blendDuration: 0.3) in withAnimation. The response and dampingFraction parameters mirror stiffness and damping from UIKit. For interactive drag animations, use .interactiveSpring() — a special version with gesture-driven initial velocity optimized for drag animations.
Check SpringForce.finalPosition — if it is not set or equals the current value, the animation may not start. Call springAnim.cancel() in the Activity/Fragment onPause(). For SpringAnimation in RecyclerView, use view.setOnDetachListener { animator.cancel() }. For Jetpack Compose, SpringAnimation automatically pauses on composition changes.
Yes. On iOS, call view.layer.removeAnimation(forKey:) — the animation stops at the current frame. On Android, call springAnim.cancel() — the object stays at the current position. For a smooth transition to a new target value, update the finalPosition and do not call cancel — SpringAnimation automatically recalculates the trajectory. For SkipToEnd, use springAnim.skipToEnd() on Android.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also