Spring Animation — تکنیک انیمیشنی که رفتار فیزیکی فنر را مدلسازی میکند: جرم، سفتی (stiffness)، میرایی (damping) و سرعت اولیه. برخلاف درونیابیهای خطی و منحنیهای بزیه، انیمیشن فنری حرکت طبیعی با اثر نوسانات میرا ایجاد میکند که در هر دستگاهی ارگانیک به نظر میرسد. طبق Material Design Guidelines (2026)، Spring Animation در 73% از رابطهای متحرک در ۱۰۰ برنامه برتر App Store استفاده میشود. درباره سایر انواع انیمیشن در راهنمای عمومی انیمیشن بخوانید.
نکات اصلی
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 برای هرگونه انیمیشن مرتبط با حرکت توصیه میشود: جابجایی، مقیاسگذاری، ظاهر/ناپدید شدن.
رفتار فنر با چهار پارامتر تعیین میشود. Stiffness (سفتی) — نیروی بازگشت به تعادل: مقدار 100–300 فنر نرم برای لیستها، 500–1000 فنر سخت برای دکمهها و سوئیچها. Damping (میرایی) — مقاومت در برابر حرکت: 0 — نوسانات بینهایت، 0.1–0.5 — نوسانات میرا با اثر bounce، 1 — میرایی بحرانی (بدون نوسان، ترمز آرام).
| پارامتر | محدوده | اثر | مثال استفاده |
|---|---|---|---|
| Stiffness | 100–1000 | سرعت بازگشت به تعادل | 200 = لیست، 600 = دکمه |
| Damping | 0.0–1.0 | میرایی نوسانات | 0.3 = bounce، 1.0 = بدون نوسان |
| Mass | 0.1–10.0 | اینرسی جسم | 1.0 = استاندارد، 3.0 = عنصر سنگین |
| Initial Velocity | هر مقدار | سرعت اولیه بر حسب پیکسل/ثانیه | سرعت انگشت در drag-to-dismiss |
میرایی بحرانی (damping = 1.0) — حالت بهینه برای بیشتر انیمیشنهای UI: جسم در سریعترین زمان ممکن بدون overshoot به موقعیت هدف بازمیگردد. برای اثر bounce (مثلاً pull-to-refresh) از damping = 0.3–0.5 استفاده کنید. برای ظاهر شدن پنجرههای modal — damping = 0.6–0.8 با stiffness = 300–500. جرم به ندرت تغییر میکند — مقدار استاندارد 1.0 تقریباً برای همه موارد مناسب است.
در 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 به عنوان ویژگیهای جداگانه دسترسی فراهم میکند.
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 اثر bounce قابل توجهی ایجاد میکند. برای اتصال با UIPanGestureRecognizer، velocityRecognizer.velocity(in: view) را به عنوان initialSpringVelocity ارسال کنید — به این ترتیب انیمیشن حرکت انگشت را با همان سرعت ادامه میدهد.
در Android SpringAnimation در کتابخانه AndroidX Dynamic Animation (androidx.dynamicanimation) قرار دارد. کلاس اصلی — SpringAnimation، که هر ویژگی float از View یا شیء سفارشی را انیمیشن میکند. پارامترها از طریق SpringForce تنظیم میشوند — stiffness (پیشفرض STIFFNESS_MEDIUM = 600) و dampingRatio (پیشفرض DAMPING_RATIO_MEDIUM_BOUNCY = 0.5).
// 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 و منحنیهای Easing (easeInOut, FastOutSlowIn, decelerate) وظایف متفاوتی را حل میکنند. منحنیهای Easing — مسیری ریاضی با زمان ثابت هستند. Spring — شبیهسازی فیزیکی است که در آن زمان به پارامترها و شرایط اولیه بستگی دارد. برای انیمیشنهای ساده ظاهر/ناپدید شدن، منحنیهای Easing کافی هستند. برای انیمیشنهای مرتبط با حرکت کاربر (drag, swipe, scroll)، Spring Animation بازخورد طبیعیتری میدهد.
| سناریو | توصیه | پارامترها |
|---|---|---|
| ظهور عنصر | Spring (damping = 0.7, stiffness = 400) | ظهور نرم با overshoot جزئی |
| Swipe-to-dismiss | Spring + سرعت اولیه | setStartVelocity(velocity), damping = 0.5 |
| انیمیشن آیکون | Spring (damping = 0.3, stiffness = 600) | bounce قابل توجه برای جلوههای تأکیدی |
| فیزیک اسکرول | Spring (سیستمی) | شخصیسازی نکنید — Android خود مدیریت میکند |
| تغییر اندازه | Easing (FastOutSlowIn) | Easing برای تغییرات layout قابل اعتمادتر است |
ضدالگوها: از Spring Animation برای انیمیشن پارامترهای layout (عرض/ارتفاع View از طریق LayoutParams) استفاده نکنید — این باعث محاسبه مجدد سلسلهمراتب در هر فریم میشود و به لرزش و افت FPS منجر میشود. برای انیمیشن رنگ نیز منحنیهای Easing بهتر هستند. برای انیمیشن موقعیت اسکرول از Scroller سیستمی با fling استفاده کنید — Spring Animation در اینجا مزیتی ندارد. برای حداکثر کارایی در Android، تعداد SpringAnimation همزمان فعال را به 10–15 محدود کنید.
سوالات متداول
Stiffness — سفتی فنر (نیروی بازگشت). مقدار زیاد (1000) — حرکت سریع و ناگهانی. مقدار کم (100) — نرم و آهسته. Damping — میرایی (مقاومت). کم (0.2) — نوسانات طولانی با bounce. زیاد (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) یا .spring(blendDuration: 0.3) در withAnimation تنظیم میشود. response و dampingFraction معادل stiffness و damping از UIKit هستند. برای interactiveSpring() از .interactiveSpring() استفاده کنید — نسخه مخصوص با سرعت اولیه از حرکت، بهینهسازی شده برای انیمیشنهای drag.
SpringForce.finalPosition را بررسی کنید — اگر تنظیم نشده یا برابر مقدار فعلی باشد، انیمیشن ممکن است شروع نشود. در onPause() Activity/Fragment springAnim.cancel() را فراخوانی کنید. برای SpringAnimation در RecyclerView از view.setOnDetachListener { animator.cancel() } استفاده کنید. برای Jetpack Compose، SpringAnimation به طور خودکار هنگام composition متوقف میشود.
بله. در iOS view.layer.removeAnimation(forKey:) را فراخوانی کنید — انیمیشن در فریم فعلی متوقف میشود. در Android springAnim.cancel() را فراخوانی کنید — جسم در موقعیت فعلی باقی میماند. برای انتقال نرم به مقدار هدف جدید، finalPosition را بهروز کنید و cancel را فراخوانی نکنید — SpringAnimation مسیر را به طور خودکار دوباره محاسبه میکند. برای SkipToEnd در Android از springAnim.skipToEnd() استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید