0Pricing
Android Academy · Leçon

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 :

  • Animatable offre un contrôle impératif piloté par des coroutines via animateTo / snapTo.
  • spring se règle avec dampingRatio (rebond) et stiffness (vitesse).
  • pointerInput + detectDragGestures transforment les entrées tactiles en mouvements ; animateDecay ajoute l’effet de lancer.
  • anchoredDraggable effectue un arrêt sur des positions fixes pour les interactions par balayage.
  • rememberInfiniteTransition pilote 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

  1. Pourquoi les animations sont importantes
  2. Principes d’animate*AsState
  3. AnimatedVisibility et transitions
  4. Animations gestuelles et à ressort
← Retour à Android Academy