Spring Animation é uma técnica de animação que simula o comportamento físico de uma mola: massa, rigidez (stiffness), amortecimento (damping) e velocidade inicial. Diferente das interpolações lineares e curvas Bezier, a animação com mola cria um movimento natural com um efeito de oscilação amortecida que parece orgânico em qualquer dispositivo. De acordo com as Material Design Guidelines (2026), a Spring Animation é usada em 73% das interfaces animadas dos top 100 aplicativos da App Store. Saiba mais sobre outros tipos de animação no guia geral de animação.
Pontos principais
Spring Animation é uma animação cujo movimento é descrito pela equação do oscilador harmônico amortecido. Em vez de interpolar um valor de 0 a 1 durante um tempo fixo, a Spring Animation calcula a posição do objeto em cada momento com base em parâmetros físicos: massa, rigidez, amortecimento e velocidade inicial. O resultado é um movimento natural e orgânico que não parece "mecânico".
Ao contrário das curvas Easing (easeIn, easeOut, cubic-bezier), onde a duração é fixa e a trajetória é predeterminada, a Spring Animation se adapta dinamicamente: se o valor alvo mudar durante a animação, a mola recalcula suavemente a trajetória a partir da posição atual. Isso é especialmente importante para animações que podem ser interrompidas — drag-to-dismiss, pull-to-refresh, cartões deslizáveis (swipeable cards).
De acordo com a Apple WWDC 2025, o uso de animação com mola em vez de curvas fixas reduz a latência percebida em 30–50% graças à aceleração natural no início e à desaceleração suave no final. A Spring Animation é recomendada para qualquer animação relacionada a movimento: deslocamento, escala, aparecimento/desaparecimento.
O comportamento da mola é definido por quatro parâmetros. Stiffness (rigidez) é a força de retorno ao equilíbrio: um valor de 100–300 dá uma mola suave para listas, 500–1000 dá uma mola rígida para botões e interruptores. Damping (amortecimento) é a resistência ao movimento: 0 — oscilações infinitas, 0.1–0.5 — oscilações amortecidas perceptíveis (efeito de ricochete), 1 — amortecimento crítico (sem oscilações, desaceleração suave).
| Parâmetro | Faixa | Efeito | Exemplo de uso |
|---|---|---|---|
| Stiffness | 100–1000 | Velocidade de retorno ao equilíbrio | 200 = lista, 600 = botão |
| Damping | 0.0–1.0 | Amortecimento de oscilações | 0.3 = ricochete, 1.0 = sem oscilações |
| Mass | 0.1–10.0 | Inércia do objeto | 1.0 = padrão, 3.0 = elemento pesado |
| Initial Velocity | Qualquer | Velocidade inicial em pixels/s | Velocidade do dedo ao fazer drag-to-dismiss |
Amortecimento crítico (damping = 1.0) é o modo ideal para a maioria das animações de UI: o objeto retorna à posição alvo o mais rápido possível sem ultrapassagem. Para o efeito de ricochete (por exemplo, pull-to-refresh), use damping = 0.3–0.5. Para aparecimento de janelas modais — damping = 0.6–0.8 com stiffness = 300–500. A massa raramente é alterada — o valor padrão de 1.0 funciona para quase todos os casos.
No iOS, a Spring Animation está disponível através de UIView.animate(withDuration:delay:usingSpringWithDamping:initialSpringVelocity:) desde o iOS 7 e CASpringAnimation (Core Animation) desde o iOS 9. A versão UIView é mais simples e popular para animar propriedades de View (transform, alpha, center). A CASpringAnimation é mais precisa e fornece acesso a stiffness, damping e mass como propriedades separadas.
import UIKit
// UIView.animate com parâmetros de mola
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 — controle preciso sobre os parâmetros
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 — duração calculada
spring.duration = spring.settlingDuration
view.layer.add(spring, forKey: "scaleSpring")
CASpringAnimation.settlingDuration é uma propriedade única da Core Animation que calcula a duração mínima de animação necessária para a mola atingir o equilíbrio. Isso significa que você não precisa escolher a duração manualmente — o sistema determina quanto tempo o amortecimento das oscilações levará. Para UIView.animate, a duração é definida explicitamente, e um damping de 0.3–0.7 produz um efeito de ricochete perceptível. Para integração com UIPanGestureRecognizer, passe velocityRecognizer.velocity(in: view) como initialSpringVelocity — assim a animação continua o movimento do dedo na mesma velocidade.
No Android, a SpringAnimation faz parte da biblioteca AndroidX Dynamic Animation (androidx.dynamicanimation). A classe principal é SpringAnimation, que anima qualquer propriedade float de uma View ou de um objeto personalizado. Os parâmetros são configurados através de SpringForce — stiffness (padrão STIFFNESS_MEDIUM = 600) e dampingRatio (padrão DAMPING_RATIO_MEDIUM_BOUNCY = 0.5).
// build.gradle.kts
implementation("androidx.dynamicanimation:dynamicanimation:1.1.0")
// Kotlin: SpringAnimation com SpringForce personalizado
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 com velocidade inicial do gesto
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) "> Retorno à posição inicial
setStartVelocity(velocity)
start()
}
true
}
else -> false
}
}
"> SpringAnimation para propriedade personalizada
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() }
Constantes 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). Para RecyclerView com animação de itens, use SpringAnimation com DynamicAnimation.OnAnimationEndListener para liberar recursos. Para animar várias propriedades, use SpringAnimation.createFor(view, property).
Jetpack Compose: a partir do Compose 1.0, a animação com mola está disponível através de Animatable com spring() — animationSpec = spring(dampingRatio = 0.5f, stiffness = 300f). Esta é a abordagem preferida para projetos Compose, já que SpringAnimation do DynamicAnimation é compatível apenas com o sistema View. A versão Compose do spring usa o mesmo motor físico, mas opera no contexto do Compose Animation System, pausando automaticamente ao ser desanexado.
Spring Animation e curvas Easing (easeInOut, FastOutSlowIn, decelerate) resolvem problemas diferentes. Curvas Easing são trajetórias matematicamente definidas com tempo fixo. Spring é uma simulação física onde o tempo depende dos parâmetros e condições iniciais. Para animações simples de aparecimento/desaparecimento, curvas Easing são suficientes. Para animações ligadas ao movimento do usuário (drag, swipe, scroll), a Spring Animation fornece uma resposta mais natural.
| Cenário | Recomendação | Parâmetros |
|---|---|---|
| Aparecimento de elemento | Spring (damping = 0.7, stiffness = 400) | Aparecimento suave com ligeira ultrapassagem |
| Swipe-to-dismiss | Spring + velocidade inicial | setStartVelocity(velocity), damping = 0.5 |
| Animação de ícone | Spring (damping = 0.3, stiffness = 600) | Ricochete perceptível para efeitos de destaque |
| Física de scroll | Spring (sistema) | Não personalizar — Android gerencia |
| Alteração de tamanho | Easing (FastOutSlowIn) | Easing é mais confiável para mudanças de layout |
Anti-padrões: não use Spring Animation para animar parâmetros de layout (largura/altura de View via LayoutParams) — isso causa recálculo de hierarquia em cada quadro, levando a travamentos e quedas de FPS. Para animação de cor, curvas Easing também são preferíveis. Para animação de posição de scroll, use o Scroller do sistema com fling — Spring Animation não oferece vantagens aqui. Para máximo desempenho no Android, limite o número de instâncias SpringAnimation ativas simultaneamente a 10–15.
Perguntas frequentes
Stiffness é a rigidez da mola (força de retorno). Um valor alto (1000) produz movimento rápido e brusco. Um valor baixo (100) produz movimento suave e lento. Damping é a resistência ao movimento. Um valor baixo (0.2) produz oscilações longas com ricochete. Um valor alto (1.0) produz ausência de oscilações, desaceleração suave. Stiffness determina a velocidade, damping determina a presença e intensidade das oscilações.
Comece com damping = 0.5 e stiffness = 400 — este é um conjunto inicial universal para iOS e Android. Para aparecimento de elementos, aumente damping para 0.7. Para efeitos de destaque (curtir, adicionar ao carrinho), reduza damping para 0.3 com stiffness = 600. Para Material Motion, use damping = 0.6 e stiffness = 300. Teste a animação em um dispositivo real, não em um simulador.
Sim, no SwiftUI a animação com mola é configurada através de .animation(.spring(dampingFraction: 0.5, response: 0.5), value: state) ou .spring(blendDuration: 0.3) no withAnimation. Os parâmetros response e dampingFraction refletem stiffness e damping do UIKit. Para animações de arrasto interativas, use .interactiveSpring() — uma versão especial com velocidade inicial orientada por gestos otimizada para animações de arrasto.
Verifique SpringForce.finalPosition — se não estiver definido ou for igual ao valor atual, a animação pode não iniciar. Chame springAnim.cancel() no onPause() da Activity/Fragment. Para SpringAnimation em RecyclerView, use view.setOnDetachListener { animator.cancel() }. Para Jetpack Compose, a SpringAnimation pausa automaticamente em mudanças de composição.
Sim. No iOS, chame view.layer.removeAnimation(forKey:) — a animação para no quadro atual. No Android, chame springAnim.cancel() — o objeto permanece na posição atual. Para uma transição suave para um novo valor alvo, atualize finalPosition e não chame cancel — a SpringAnimation recalcula automaticamente a trajetória. Para SkipToEnd, use springAnim.skipToEnd() no Android.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também