Spring Animationは、バネの物理的挙動をシミュレートするアニメーション技術です:質量、剛性(stiffness)、減衰(damping)、初速度。線形補間やベジェ曲線とは異なり、スプリングアニメーションは減衰振動効果を伴う自然な動きを生み出し、どのデバイスでも有機的に見えます。Material Design Guidelines(2026年)によると、Spring AnimationはApp Storeのトップ100アプリの73%のアニメーションインターフェースで使用されています。他の種類のアニメーションについては、一般的なアニメーションガイドをご覧ください。
重要なポイント
Spring Animationは、減衰調和振動子の方程式によって動きが記述されるアニメーションです。固定時間で0から1まで値を補間する代わりに、Spring Animationは物理パラメータ(質量、剛性、減衰、初速度)に基づいて各瞬間のオブジェクトの位置を計算します。結果は自然で有機的な動きとなり、「機械的」には見えません。
Easingカーブ(easeIn、easeOut、cubic-bezier)とは異なり、持続時間が固定され軌道が事前に決定されているのに対し、Spring Animationは動的に適応します:アニメーション中に目標値が変更されると、バネは現在位置から軌道を滑らかに再計算します。これは、ドラッグ・トゥ・ディスミス、プル・トゥ・リフレッシュ、スワイプ可能なカードなど、中断される可能性のあるアニメーションにとって特に重要です。
Apple WWDC 2025によると、固定カーブの代わりにスプリングアニメーションを使用すると、開始時の自然な加速と終了時の滑らかな減速により、知覚遅延が30〜50%削減されます。Spring Animationは、移動、スケーリング、表示/非表示など、動きに関連するあらゆるアニメーションに推奨されます。
バネの挙動は4つのパラメータによって定義されます。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 | 任意 | ピクセル/秒での初速度 | ドラッグ・トゥ・ディスミス時の指の速度 |
臨界減衰(damping = 1.0)は、ほとんどのUIアニメーションに最適なモードです:オブジェクトはオーバーシュートなしで可能な限り迅速に目標位置に戻ります。バウンス効果(例:プル・トゥ・リフレッシュ)には、damping = 0.3〜0.5を使用します。モーダルウィンドウの表示には、damping = 0.6〜0.8、stiffness = 300〜500を使用します。質量はめったに変更されません — 標準値の1.0はほぼすべてのケースで機能します。
iOSでは、Spring AnimationはiOS 7以降のUIView.animate(withDuration:delay:usingSpringWithDamping:initialSpringVelocity:)と、iOS 9以降のCASpringAnimation(Core Animation)を通じて利用可能です。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では、時間は明示的に設定され、0.3〜0.7のdampingで顕著なバウンス効果が得られます。UIPanGestureRecognizerとの統合では、velocityRecognizer.velocity(in: view)をinitialSpringVelocityとして渡します — これによりアニメーションは同じ速度で指の動きを継続します。
Androidでは、SpringAnimationはAndroidX Dynamic Animationライブラリ(androidx.dynamicanimation)の一部です。メインクラスはSpringAnimationで、Viewまたはカスタムオブジェクトの任意のfloatプロパティをアニメーション化します。パラメータはSpringForceを通じて設定されます — stiffness(デフォルトSTIFFNESS_MEDIUM = 600)とdampingRatio(デフォルトDAMPING_RATIO_MEDIUM_BOUNCY = 0.5)。
// build.gradle.kts
implementation("androidx.dynamicanimation:dynamicanimation:1.1.0")
// Kotlin:カスタムSpringForceを使用したSpringAnimation
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では、DynamicAnimation.OnAnimationEndListenerと共にSpringAnimationを使用してリソースを解放します。複数のプロパティをアニメーション化するには、SpringAnimation.createFor(view, property)を使用します。
Jetpack Compose:Compose 1.0以降、スプリングアニメーションはspring()を使ったAnimatableを通じて利用可能です — animationSpec = spring(dampingRatio = 0.5f, stiffness = 300f)。これはComposeプロジェクトに推奨される方法です。DynamicAnimationのSpringAnimationはViewシステムとのみ互換性があるためです。springのComposeバージョンは同じ物理エンジンを使用しますが、Compose Animation Systemのコンテキストで動作し、デタッチ時に自動的に一時停止します。
Spring AnimationとEasingカーブ(easeInOut、FastOutSlowIn、decelerate)は異なる課題を解決します。Easingカーブは固定時間で数学的に定義された軌道です。Springはパラメータと初期条件に時間が依存する物理シミュレーションです。単純な表示/非表示アニメーションにはEasingカーブで十分です。ユーザーの動作(ドラッグ、スワイプ、スクロール)に関連するアニメーションには、Spring Animationがより自然な応答を提供します。
| シナリオ | 推奨 | パラメータ |
|---|---|---|
| 要素の表示 | Spring(damping = 0.7、stiffness = 400) | わずかなオーバーシュートで滑らかに表示 |
| スワイプ・トゥ・ディスミス | Spring + 初速度 | setStartVelocity(velocity)、damping = 0.5 |
| アイコンのアニメーション | Spring(damping = 0.3、stiffness = 600) | アクセント効果のための顕著なバウンス |
| スクロール物理 | Spring(システム) | カスタマイズしない — Androidが管理 |
| サイズ変更 | Easing(FastOutSlowIn) | レイアウト変更にはEasingの方が信頼性が高い |
アンチパターン:レイアウトパラメータ(LayoutParamsによるViewの幅/高さ)のアニメーションにSpring Animationを使用しないでください — これにより毎フレーム階層の再計算が発生し、カクツキやFPS低下を引き起こします。色のアニメーションにもEasingカーブが適しています。スクロール位置のアニメーションには、フリング付きのシステムScrollerを使用してください — 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)またはwithAnimation内の.spring(blendDuration: 0.3)で設定します。responseとdampingFractionはUIKitのstiffnessとdampingに対応します。インタラクティブなドラッグアニメーションには、.interactiveSpring()を使用します — ドラッグアニメーション用に最適化された、ジェスチャー駆動の初速度を持つ特別なバージョンです。
SpringForce.finalPositionを確認してください — 設定されていないか現在の値と等しい場合、アニメーションが開始しない可能性があります。Activity/FragmentのonPause()でspringAnim.cancel()を呼び出してください。RecyclerView内のSpringAnimationには、view.setOnDetachListener { animator.cancel() }を使用してください。Jetpack Composeでは、SpringAnimationはコンポジション変更時に自動的に一時停止します。
はい。iOSではview.layer.removeAnimation(forKey:)を呼び出すと、アニメーションは現在のフレームで停止します。AndroidではspringAnim.cancel()を呼び出すと、オブジェクトは現在位置に留まります。新しい目標値へのスムーズな移行には、finalPositionを更新し、cancelを呼び出さないでください — SpringAnimationが自動的に軌道を再計算します。SkipToEndには、AndroidでspringAnim.skipToEnd()を使用します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。