Spring Animation — τεχνική κίνησης που μοντελοποιεί τη φυσική συμπεριφορά ενός ελατηρίου: μάζα, ακαμψία (stiffness), απόσβεση (damping) και αρχική ταχύτητα. Σε αντίθεση με τις γραμμικές παρεμβολές και τις καμπύλες Bézier, η κίνηση ελατηρίου δημιουργεί φυσική κίνηση με φαινόμενο αποσβενόμενων ταλαντώσεων που φαίνεται οργανική σε κάθε συσκευή. Σύμφωνα με το Material Design Guidelines (2026), το Spring Animation χρησιμοποιείται στο 73% των κινούμενων διεπαφών των κορυφαίων 100 εφαρμογών του 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 — αισθητές αποσβενόμενες ταλαντώσεις (φαινόμενο αναπήδησης), 1 — κρίσιμη απόσβεση (χωρίς ταλαντώσεις, ομαλή επιβράδυνση).
| Παράμετρος | Εύρος | Επίδραση | Παράδειγμα χρήσης |
|---|---|---|---|
| Stiffness | 100–1000 | Ταχύτητα επιστροφής σε ισορροπία | 200 = λίστα, 600 = κουμπί |
| Damping | 0.0–1.0 | Απόσβεση ταλαντώσεων | 0.3 = αναπήδηση, 1.0 = χωρίς ταλαντώσεις |
| Mass | 0.1–10.0 | Αδράνεια αντικειμένου | 1.0 = standard, 3.0 = βαρύ στοιχείο |
| Initial Velocity | Οποιαδήποτε | Αρχική ταχύτητα σε pixels/s | Ταχύτητα δαχτύλου σε drag-to-dismiss |
Κρίσιμη απόσβεση (damping = 1.0) — βέλτιστη λειτουργία για τις περισσότερες κινήσεις UI: το αντικείμενο επιστρέφει στη θέση-στόχο όσο το δυνατόν γρηγορότερα χωρίς υπέρβαση. Για φαινόμενο αναπήδησης (π.χ. pull-to-refresh) χρησιμοποιήστε damping = 0.3–0.5. Για εμφάνιση modal παραθύρων — damping = 0.6–0.8 με stiffness = 300–500. Η μάζα σπάνια αλλάζει — η standard τιμή 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 δίνει αισθητό φαινόμενο αναπήδησης. Για σύνδεση με 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, σταματώντας αυτόματα κατά την αποκόλληση.
Spring Animation και καμπύλες Easing (easeInOut, FastOutSlowIn, decelerate) λύνουν διαφορετικά προβλήματα. Οι καμπύλες Easing — μαθηματικά καθορισμένη τροχιά με σταθερό χρόνο. Το Spring — φυσική προσομοίωση, όπου ο χρόνος εξαρτάται από παραμέτρους και αρχικές συνθήκες. Για απλές κινήσεις εμφάνισης/εξαφάνισης, οι καμπύλες Easing είναι επαρκείς. Για κινήσεις που σχετίζονται με κίνηση χρήστη (drag, swipe, scroll), το Spring Animation δίνει πιο φυσική απόκριση.
| Σενάριο | Σύσταση | Παράμετροι |
|---|---|---|
| Εμφάνιση στοιχείου | Spring (damping = 0.7, stiffness = 400) | Ομαλή εμφάνιση με ελαφριά υπέρβαση |
| Swipe-to-dismiss | Spring + αρχική ταχύτητα | setStartVelocity(velocity), damping = 0.5 |
| Κίνηση εικονιδίου | Spring (damping = 0.3, stiffness = 600) | Αισθητή αναπήδηση για εφέ έμφασης |
| Φυσική κύλισης | Spring (συστημική) | Μην προσαρμόζετε — το Android διαχειρίζεται μόνο του |
| Αλλαγή μεγέθους | Easing (FastOutSlowIn) | Το Easing είναι πιο αξιόπιστο για αλλαγές layout |
Anti-patterns: μην χρησιμοποιείτε Spring Animation για κίνηση παραμέτρων layout (πλάτος/ύψος View μέσω LayoutParams) — αυτό προκαλεί επαναϋπολογισμό της ιεραρχίας σε κάθε καρέ, οδηγώντας σε τράνταγμα και πτώση FPS. Για κίνηση χρώματος επίσης οι καμπύλες Easing είναι καλύτερες. Για κίνηση θέσης κύλισης χρησιμοποιήστε το συστημικό Scroller με fling — το Spring Animation δεν προσφέρει πλεονεκτήματα εδώ. Για μέγιστη απόδοση σε Android, περιορίστε τον αριθμό ταυτόχρονα ενεργών SpringAnimation σε 10–15.
Συχνές ερωτήσεις
Stiffness — ακαμψία ελατηρίου (δύναμη επιστροφής). Υψηλή τιμή (1000) — γρήγορη, απότομη κίνηση. Χαμηλή (100) — απαλή, αργή. Damping — απόσβεση (αντίσταση). Χαμηλή (0.2) — μακριές ταλαντώσεις με αναπήδηση. Υψηλή (1.0) — χωρίς ταλαντώσεις, ομαλή επιβράδυνση. Το Stiffness καθορίζει την ταχύτητα, το damping — την παρουσία και ένταση των ταλαντώσεων.
Ξεκινήστε με damping = 0.5 και stiffness = 400 — αυτό είναι ένα universal σετ εκκίνησης για iOS και Android. Για εμφάνιση στοιχείων αυξήστε το damping σε 0.7. Για εφέ έμφασης (like, προσθήκη στο καλάθι) μειώστε το 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 — αν δεν έχει οριστεί ή είναι ίσο με την τρέχουσα τιμή, η κίνηση μπορεί να μην ξεκινήσει. Καλέστε springAnim.cancel() στο onPause() του Activity/Fragment. Για SpringAnimation σε RecyclerView χρησιμοποιήστε view.setOnDetachListener { animator.cancel() }. Για Jetpack Compose το SpringAnimation σταματά αυτόματα κατά τη σύνθεση.
Ναι. Στο iOS καλέστε view.layer.removeAnimation(forKey:) — η κίνηση θα σταματήσει στο τρέχον καρέ. Στο Android καλέστε springAnim.cancel() — το αντικείμενο παραμένει στην τρέχουσα θέση. Για ομαλή μετάβαση σε νέα τιμή-στόχο ενημερώστε το finalPosition και μην καλέσετε cancel — το SpringAnimation θα υπολογίσει ξανά αυτόματα την τροχιά. Για SkipToEnd χρησιμοποιήστε springAnim.skipToEnd() σε Android.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης