Personnaliser les animations

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

Personnaliser les 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),
    label = "alpha"
)

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 : Définir différents ratios de freinage des ressorts

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 raideur de ressort différente

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

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 le toucher de l'utilisateur, et que nous interrompons l'animation en cours, vous pouvez constater que l'utilisation de tween ne répond pas aussi bien que celle de spring.

Figure 3. Définir des spécifications tween par rapport à spring pour l'animation et l'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", car il se situe entre deux valeurs.

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

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

Pour en savoir plus, consultez la section Easing.

Animer vers des valeurs spécifiques à des moments spécifiques 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 using LinearOutSlowInEasing // for 0-15 ms
        0.2f at 15 using FastOutLinearInEasing // for 15-75 ms
        0.4f at 75 // ms
        0.4f at 225 // ms
    },
    label = "keyframe"
)

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
    ),
    label = "repeatable spec"
)

Répéter une animation indéfiniment 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
    ),
    label = "infinite repeatable"
)

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.

Ancrer immédiatement 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),
    label = "snap spec"
)

Définir une fonction d'atténuation 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
        ),
        label = "custom easing"
    )
    // ……
}

Compose intègre plusieurs fonctions Easing qui couvrent la plupart des cas d'utilisation. Pour déterminer quel lissage de vitesse utiliser selon votre scénario, consultez la section sur la vitesse dans Material Design.

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

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

La plupart des API d'animation Compose acceptent les types Float, Color et Dp, ainsi que d'autres types de données de base en tant que valeurs d'animation par défaut, mais vous devez parfois animer d'autres types de données, y compris des types 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. Vous pouvez accéder aux convertisseurs intégrés des types de données de base à 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)
            }
        ),
        label = "size"
    )
}

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