Personnaliser les animations

Bon nombre des API Animation acceptent des paramètres qui permettent de personnaliser leur comportement.

Personnalisez des animations avec le paramètre AnimationSpec.

La plupart des API d'animation permettent aux développeurs de personnaliser les spécifications d'animation à l'aide d'un paramètre AnimationSpec facultatif.

val alpha: Float by animateFloatAsState(
    targetValue = if (enabled) 1f else 0.5f,
    // Configure the animation duration and easing.
    animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing)
)

Il existe plusieurs types de AnimationSpec, qui permettent de créer autant d'animations différentes.

Créer une animation basée sur la physique avec spring

spring crée une animation basée sur des mécanismes physiques entre les valeurs de début et de fin. Deux paramètres sont acceptés : dampingRatio et stiffness.

dampingRatio définit la force du rebond. La valeur par défaut est Spring.DampingRatioNoBouncy.

Figure 1 : Régler différents rapports d'amortissement du ressort.

stiffness définit la vitesse à laquelle le ressort doit se déplacer vers la valeur de fin. La valeur par défaut est Spring.StiffnessMedium.

Figure 2. Définir une autre raideur de ressort

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioHighBouncy,
        stiffness = Spring.StiffnessMedium
    )
)

spring permet de gérer les interruptions plus en douceur par rapport aux types AnimationSpec basés sur la durée, car il maintient la vitesse lorsque la valeur cible change entre les animations. De nombreuses API d'animation, dont animate*AsState et updateTransition, utilisent spring comme AnimationSpec par défaut.

Par exemple, si nous appliquons une configuration spring à l'animation suivante qui est déclenchée par l'appui de l'utilisateur, lorsque vous interrompez l'animation au fur et à mesure de sa progression, vous pouvez constater que l'utilisation de tween ne répond pas aussi bien que l'utilisation de spring.

Figure 3. Définir les spécifications tween et spring pour l'animation, et les interrompre

Animer entre les valeurs de début et de fin avec une courbe de lissage de vitesse avec tween

tween crée une animation entre les valeurs de début et de fin durant la période durationMillis spécifiée en suivant une courbe de lissage de vitesse. tween est l'abréviation du mot "entre" puisqu'il est entre deux valeurs.

Vous pouvez également spécifier delayMillis pour différer le début de l'animation.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = tween(
        durationMillis = 300,
        delayMillis = 50,
        easing = LinearOutSlowInEasing
    )
)

Pour en savoir plus, consultez la section Lissage de vitesse.

Effectuer une animation à des valeurs spécifiques à certains moments avec keyframes

keyframes crée une animation en fonction des valeurs d'instantané spécifiées à différents horodatages pendant la durée de l'animation. À un moment donné, la valeur d'animation est interpolée entre deux valeurs d'image clé. Vous pouvez spécifier la courbe d'interpolation de chacune de ces images clés en utilisant Easing.

De manière facultative, vous pouvez spécifier les valeurs à 0 ms et pour toute la durée. Dans le cas contraire, elles seront définies par défaut sur les valeurs de début et de fin de l'animation, respectivement.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = keyframes {
        durationMillis = 375
        0.0f at 0 with LinearOutSlowInEasing // for 0-15 ms
        0.2f at 15 with FastOutLinearInEasing // for 15-75 ms
        0.4f at 75 // ms
        0.4f at 225 // ms
    }
)

Répéter une animation avec repeatable

repeatable exécute plusieurs fois une animation basée sur la durée (par exemple, tween ou keyframes) jusqu'à atteindre le nombre d'itérations spécifié. Vous pouvez transmettre le paramètre repeatMode pour indiquer si l'animation doit être répétée en commençant par le début (RepeatMode.Restart) ou par la fin (RepeatMode.Reverse).

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = repeatable(
        iterations = 3,
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    )
)

Répéter une animation à l'infini avec infiniteRepeatable

infiniteRepeatable est semblable à repeatable, mais l'animation se répète indéfiniment.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    )
)

Dans les tests avec ComposeTestRule, les animations qui utilisent infiniteRepeatable ne sont pas exécutées. La valeur initiale de chaque valeur animée sera utilisée pour afficher le composant.

Aligner immédiatement sur la valeur de fin avec snap

snap est un AnimationSpec spécial qui remplace immédiatement la valeur par la valeur de fin. Vous pouvez spécifier delayMillis pour retarder le début de l'animation.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = snap(delayMillis = 50)
)

Définir une fonction de lissage de vitesse personnalisée

Les opérations AnimationSpec basées sur la durée (telles que tween ou keyframes) utilisent Easing pour ajuster une fraction d'une animation. Cela permet à la valeur d'animation d'accélérer et de ralentir plutôt que de se déplacer à une vitesse constante. La fraction est une valeur comprise entre 0 (début) et 1.0 (fin). Elle indique le point actuellement atteint dans l'animation.

En réalité, Easing est une fonction qui accepte une valeur de fraction comprise entre 0 et 1.0 et renvoie un float. La valeur renvoyée peut se trouver en dehors des limites pour représenter un dépassement vers le haut ou vers le bas. Vous pouvez créer un Easing personnalisé comme dans le code ci-dessous.

val CustomEasing = Easing { fraction -> fraction * fraction }

@Composable
fun EasingUsage() {
    val value by animateFloatAsState(
        targetValue = 1f,
        animationSpec = tween(
            durationMillis = 300,
            easing = CustomEasing
        )
    )
    // ……
}

Compose intègre plusieurs fonctions Easing qui couvrent la plupart des cas d'utilisation. Pour en savoir plus sur le lissage de vitesse à utiliser en fonction de votre scénario, consultez la section Vitesse – Material Design.

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearEasing
  • LinearEasing
  • CubicBezierEasing
  • Voir plus

Animez des types de données personnalisés en les convertissant depuis et vers AnimationVector

La plupart des API d'animation Compose prennent en charge par défaut Float, Color, Dp et d'autres types de données de base en tant que valeurs d'animation, mais vous devez parfois animer d'autres types de données, y compris vos types de données personnalisés. Pendant l'animation, les valeurs d'animation sont représentées par un AnimationVector. Elles sont converties en AnimationVector et inversement par un TwoWayConverter associé afin que le système d'animation principal puisse les gérer de manière uniforme. Par exemple, Int est représenté par un AnimationVector1D contenant une seule valeur flottante. Le TwoWayConverter d'un Int se présente comme suit :

val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
    TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })

Color est essentiellement un ensemble de quatre valeurs (rouge, vert, bleu et alpha). Color est donc converti en un AnimationVector4D qui contient quatre floats. Ainsi, chaque type de données utilisé dans les animations est converti en AnimationVector1D, AnimationVector2D, AnimationVector3D ou AnimationVector4D en fonction de sa dimensionnalité. Cela permet d'animer indépendamment les différents composants de l'objet, chacun avec son propre suivi de la vitesse. Les convertisseurs intégrés pour les types de données de base sont accessibles à l'aide de convertisseurs tels que Color.VectorConverter ou Dp.VectorConverter.

Lorsque vous souhaitez proposer un nouveau type de données en tant que valeur d'animation, créez votre TwoWayConverter et fournissez-le à l'API. Par exemple, vous pouvez utiliser animateValueAsState pour animer votre type de données personnalisé comme suit :

data class MySize(val width: Dp, val height: Dp)

@Composable
fun MyAnimation(targetSize: MySize) {
    val animSize: MySize by animateValueAsState(
        targetSize,
        TwoWayConverter(
            convertToVector = { size: MySize ->
                // Extract a float value from each of the `Dp` fields.
                AnimationVector2D(size.width.value, size.height.value)
            },
            convertFromVector = { vector: AnimationVector2D ->
                MySize(vector.v1.dp, vector.v2.dp)
            }
        )
    )
}

La liste suivante inclut des VectorConverter intégrés: