Animations gestuelles et à ressort
Des mouvements naturels fondés sur la physique.
Animations gestuelles et à ressort est une leçon Android Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Android Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Android Academy comprend 4 leçons au total.
Mouvement interactif et physique
Les animations les plus agréables réagissent au doigt de l’utilisateur et donnent l’impression d’objets réels, avec de l’inertie et du rebond. Pour les créer, vous combinez des gestes avec des animations fondées sur la physique.
Cette leçon présente Animatable, les ressorts, la décélération après un lancer et leur association à des gestes de glissement pour créer une interface naturelle et interactive.
Animatable : contrôle de bas niveau
Alors que animate*AsState réagit à l’état, Animatable vous donne un contrôle impératif. Vous en conservez un dans remember et le pilotez depuis une coroutine avec des appels comme animateTo ou snapTo.
C’est la base des mouvements pilotés par les gestes.
@Composable
fun BounceOnClick() {
val scale = remember { Animatable(1f) }
val scope = rememberCoroutineScope()
Box(
Modifier
.graphicsLayer { scaleX = scale.value; scaleY = scale.value }
.clickable {
scope.launch {
scale.animateTo(1.3f, spring())
scale.animateTo(1f, spring())
}
}
)
}Ressorts : dampingRatio et stiffness
Une spécification spring est définie par deux valeurs :
- dampingRatio : le niveau de rebond. Plus il est faible, plus le rebond est important ;
1f(amortissement critique) ne dépasse pas la cible. - stiffness : la rapidité. Plus elle est élevée, plus la cible est atteinte rapidement.
Compose fournit des constantes comme Spring.DampingRatioMediumBouncy et Spring.StiffnessLow, si bien que vous avez rarement besoin d’utiliser des nombres bruts.
val spec = spring<Float>(
dampingRatio = Spring.DampingRatioLowBouncy,
stiffness = Spring.StiffnessMedium
)
// later, inside a coroutine:
// offset.animateTo(targetValue = 0f, animationSpec = spec)Détecter les gestes de glissement
Pour rendre les mouvements interactifs, vous devez lire les entrées tactiles. Modifier.pointerInput associé à detectDragGestures signale les déplacements lors que l’utilisateur déplace son doigt.
Vous cumulez ces déplacements dans un décalage, puis vous l’appliquez au composable.
@Composable
fun Draggable() {
var offsetX by remember { mutableStateOf(0f) }
Box(
Modifier
.offset { IntOffset(offsetX.roundToInt(), 0) }
.size(80.dp)
.background(Color.Red)
.pointerInput(Unit) {
detectDragGestures { change, drag ->
change.consume()
offsetX += drag.x
}
}
)
}Revenir en place avec un ressort
Combinez le glissement avec Animatable pour obtenir un effet de « retour en place ». Pendant le glissement, utilisez snapTo pour placer instantanément l’élément sous le doigt ; au relâchement, utilisez animateTo(0f) avec un ressort afin que l’élément rebondisse jusqu’à sa position initiale.
@Composable
fun SnapBack() {
val offsetX = remember { Animatable(0f) }
val scope = rememberCoroutineScope()
Box(
Modifier
.offset { IntOffset(offsetX.value.roundToInt(), 0) }
.size(80.dp)
.background(Color.Blue)
.pointerInput(Unit) {
detectDragGestures(
onDrag = { change, drag ->
change.consume()
scope.launch { offsetX.snapTo(offsetX.value + drag.x) }
},
onDragEnd = {
scope.launch { offsetX.animateTo(0f, spring()) }
}
)
}
)
}Lancer avec une vélocité
Lors d’un véritable lancer, le mouvement continue après que le doigt est relevé. Suivez la vélocité avec VelocityTracker, puis transmettez-la à animateDecay afin que l’élément ralentisse naturellement grâce à un DecayAnimationSpec fondé sur la friction.
val decay = rememberSplineBasedDecay<Float>()
// on drag end, with measured velocity:
// scope.launch {
// offsetX.animateDecay(
// initialVelocity = velocity,
// animationSpec = decay
// )
// }Balayer pour fermer avec des points d’ancrage
Pour les gestes de balayage avec des positions d’arrêt fixes, comme le balayage pour fermer, anchoredDraggable effectue un arrêt sur les points d’ancrage définis. Vous déclarez les états et leurs positions en pixels, puis Compose gère le ressort entre ces positions.
enum class DragValue { Start, End }
val state = remember {
AnchoredDraggableState(
initialValue = DragValue.Start,
anchors = DraggableAnchors {
DragValue.Start at 0f
DragValue.End at 300f
}
)
}Animations infinies et répétées
Pour les mouvements continus, comme un point qui pulse ou un effet de chargement animé, utilisez rememberInfiniteTransition avec infiniteRepeatable. Définissez RepeatMode sur Reverse pour faire un mouvement de va-et-vient, ou sur Restart pour recommencer la boucle.
@Composable
fun Pulse() {
val transition = rememberInfiniteTransition(label = "pulse")
val scale by transition.animateFloat(
initialValue = 1f,
targetValue = 1.2f,
animationSpec = infiniteRepeatable(
animation = tween(700),
repeatMode = RepeatMode.Reverse
),
label = "scale"
)
Box(Modifier.graphicsLayer { scaleX = scale; scaleY = scale })
}Combiner geste et ressort
Le schéma interactif par excellence : faire glisser pour déplacer, puis relâcher pour stabiliser. Suivez la vélocité pendant le glissement, puis effectuez soit un lancer avec animateDecay, soit un déplacement vers un point d’ancrage avec un spring, selon la distance ou la vitesse.
C’est exactement ce qui donne leur comportement naturel aux panneaux inférieurs, aux carrousels et aux cartes que l’on peut fermer.
// pattern:
// 1) onDrag -> animatable.snapTo(current + delta)
// 2) onDragEnd -> if (fast) animatable.animateDecay(velocity, decay)
// else animatable.animateTo(nearestAnchor, spring())Stabilisation avec un ressort en Kotlin pur
Pour comprendre comment un ressort se stabilise, voici une boucle minimale en Kotlin pur. Elle applique une force de rappel vers la cible et amortit la vélocité à chaque étape : c’est la même idée que celle exécutée par Compose à chaque image.
fun main() {
var pos = 0f
var vel = 0f
val target = 200f
val stiffness = 0.2f
val damping = 0.75f
repeat(8) { step ->
vel = (vel + (target - pos) * stiffness) * damping
pos += vel
println("step $step pos=${"%.1f".format(pos)}")
}
}Appui, pression et gestes multiples
Le glissement n’est pas le seul geste possible. pointerInput propose également detectTapGestures pour les appuis, les doubles appuis et les appuis prolongés, ainsi que detectTransformGestures pour le zoom par pincement et la rotation.
Ici, un appui prolongé déclenche une mise à l’échelle avec un ressort, en combinant la détection de gestes et un Animatable.
@Composable
fun LongPressScale() {
val scale = remember { Animatable(1f) }
val scope = rememberCoroutineScope()
Box(
Modifier
.graphicsLayer { scaleX = scale.value; scaleY = scale.value }
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
scope.launch { scale.animateTo(1.4f, spring()) }
},
onPress = {
tryAwaitRelease()
scope.launch { scale.animateTo(1f, spring()) }
}
)
}
)
}Vérification rapide
Vous créez une carte déplaçable qui doit rebondir jusqu’à son origine lorsqu’elle est relâchée. Quelle combinaison correspond au choix idiomatique dans Compose ?
Récapitulatif : gestes et ressorts
Vous pouvez désormais créer des mouvements naturels et interactifs :
Animatableoffre un contrôle impératif piloté par des coroutines viaanimateTo/snapTo.springse règle avecdampingRatio(rebond) etstiffness(vitesse).pointerInput+detectDragGesturestransforment les entrées tactiles en mouvements ;animateDecayajoute l’effet de lancer.anchoredDraggableeffectue un arrêt sur des positions fixes pour les interactions par balayage.rememberInfiniteTransitionpilote des effets en boucle, comme les pulsations et les effets de scintillement.
Vous avez terminé le chapitre sur les animations dans Jetpack Compose. Vous pouvez maintenant animer les valeurs, la visibilité, le contenu et les gestes en toute confiance.
Questions Fréquemment Posées
La leçon « Animations gestuelles et à ressort » est-elle gratuite ?
Oui — le texte complet de « Animations gestuelles et à ressort » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Android Academy, passe à CoddyKit PRO. Le cours Android Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Animations gestuelles et à ressort » ?
Des mouvements naturels fondés sur la physique. Tu pratiques Android Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Android Academy ?
Aucune expérience préalable n'est requise. Android Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Animations gestuelles et à ressort » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Android Academy ?
Oui. Chaque leçon Android Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Pourquoi les animations sont importantes
- Principes d’animate*AsState
- AnimatedVisibility et transitions
- Animations gestuelles et à ressort