Compose dispose de nombreux mécanismes d'animation intégrés, et il peut être difficile de savoir lequel choisir. Vous trouverez ci-dessous une liste de cas d'utilisation courants des animations. Pour des informations plus détaillées sur l'ensemble des différentes options d'API disponibles lisez la documentation complète sur les animations dans Compose.
Animer des propriétés composables courantes
Compose fournit des API pratiques qui vous permettent de résoudre de nombreux de l'animation. Cette section explique comment animer des éléments courants d'un composable.
Animation qui apparaît ou disparaît
![Composable vert qui s'affiche et se masque](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_column.gif?authuser=6&hl=fr)
Utilisez AnimatedVisibility
pour masquer ou afficher un composable. Enfants à l'intérieur
AnimatedVisibility
peut utiliser Modifier.animateEnterExit()
pour sa propre entrée
ou quitter la transition.
var visible by remember { mutableStateOf(true) } // Animated visibility will eventually remove the item from the composition once the animation has finished. AnimatedVisibility(visible) { // your composable here // ... }
Les paramètres d'entrée et de sortie de AnimatedVisibility
vous permettent de configurer le mode
un composable se comporte lorsqu'il apparaît et disparaît. Lire l'intégralité du
dans la documentation.
Une autre option pour animer la visibilité d'un composable consiste à animer le
alpha au fil du temps à l'aide de animateFloatAsState
:
var visible by remember { mutableStateOf(true) } val animatedAlpha by animateFloatAsState( targetValue = if (visible) 1.0f else 0f, label = "alpha" ) Box( modifier = Modifier .size(200.dp) .graphicsLayer { alpha = animatedAlpha } .clip(RoundedCornerShape(8.dp)) .background(colorGreen) .align(Alignment.TopCenter) ) { }
Cependant, la modification de la version alpha s'accompagne d'une mise en garde : le composable reste
dans la composition et continue d'occuper l'espace dans lequel elle est mise en page. Ce
peut amener les lecteurs d'écran et d'autres mécanismes d'accessibilité à prendre en compte
l'élément à l'écran. En revanche, AnimatedVisibility
finit par supprimer
l'élément de la composition.
![Animer la version alpha d'un composable](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_alpha.gif?authuser=6&hl=fr)
Animer la couleur d'arrière-plan
![Composable avec une couleur d'arrière-plan changeant au fil du temps sous forme d'animation, où les couleurs se fondent l'une dans l'autre.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=6&hl=fr)
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Cette option est plus performante que Modifier.background()
.
Modifier.background()
est acceptable pour un paramètre de couleur one-shot, mais quand
d'animer une couleur au fil du temps, cela peut entraîner plus de recompositions que
nécessaires.
Pour animer indéfiniment la couleur d'arrière-plan, consultez la section Répéter une animation. .
Animer la taille d'un composable
![Le composable vert qui anime sa taille change de façon fluide.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_content_size.gif?authuser=6&hl=fr)
Compose vous permet d'animer la taille des composables de différentes manières. Utilisez
animateContentSize()
pour les animations entre les changements de taille des composables
Par exemple, si l'une de vos zones de texte contient du texte
plusieurs lignes, vous pouvez utiliser Modifier.animateContentSize()
pour obtenir un rendu plus
transition:
var expanded by remember { mutableStateOf(false) } Box( modifier = Modifier .background(colorBlue) .animateContentSize() .height(if (expanded) 400.dp else 200.dp) .fillMaxWidth() .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { expanded = !expanded } ) { }
Vous pouvez également utiliser AnimatedContent
, avec un SizeTransform
pour décrire
des changements de taille.
Animer la position du composable
![Composable vert qui s'anime de manière fluide vers le bas et la droite](https://developer.android.com/static/develop/ui/compose/images/animations/animated_offset.gif?authuser=6&hl=fr)
Pour animer la position d'un composable, utilisez Modifier.offset{ }
en combinaison avec
animateIntOffsetAsState()
var moved by remember { mutableStateOf(false) } val pxToMove = with(LocalDensity.current) { 100.dp.toPx().roundToInt() } val offset by animateIntOffsetAsState( targetValue = if (moved) { IntOffset(pxToMove, pxToMove) } else { IntOffset.Zero }, label = "offset" ) Box( modifier = Modifier .offset { offset } .background(colorBlue) .size(100.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { moved = !moved } )
Si vous voulez vous assurer que les composables ne sont pas dessinés sur ou sous d'autres
lorsque vous animez la position ou la taille des composables, utilisez Modifier.layout{ }
. Ce
modificateur propage les changements de taille et de position au parent, ce qui affecte
avec d'autres enfants.
Par exemple, si vous déplacez un Box
dans un Column
et les autres enfants
déplacer lorsque Box
se déplace, incluez les informations de décalage avec
Modifier.layout{ }
comme suit:
var toggled by remember { mutableStateOf(false) } val interactionSource = remember { MutableInteractionSource() } Column( modifier = Modifier .padding(16.dp) .fillMaxSize() .clickable(indication = null, interactionSource = interactionSource) { toggled = !toggled } ) { val offsetTarget = if (toggled) { IntOffset(150, 150) } else { IntOffset.Zero } val offset = animateIntOffsetAsState( targetValue = offsetTarget, label = "offset" ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) Box( modifier = Modifier .layout { measurable, constraints -> val offsetValue = if (isLookingAhead) offsetTarget else offset.value val placeable = measurable.measure(constraints) layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) { placeable.placeRelative(offsetValue) } } .size(100.dp) .background(colorGreen) ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) }
![Deux cadres avec le deuxième cadre animant sa position X et Y. Le troisième cadre répond en se déplaçant lui-même d'une hauteur de Y.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_layout_modifier.gif?authuser=6&hl=fr)
Modifier.layout{ }
Animer la marge intérieure d'un composable
![Le composable vert diminue et s'agrandit lorsque l'utilisateur clique dessus, avec une marge intérieure animée](https://developer.android.com/static/develop/ui/compose/images/animations/animated_padding.gif?authuser=6&hl=fr)
Pour animer la marge intérieure d'un composable, utilisez animateDpAsState
en combinaison avec
Modifier.padding()
:
var toggled by remember { mutableStateOf(false) } val animatedPadding by animateDpAsState( if (toggled) { 0.dp } else { 20.dp }, label = "padding" ) Box( modifier = Modifier .aspectRatio(1f) .fillMaxSize() .padding(animatedPadding) .background(Color(0xff53D9A1)) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { toggled = !toggled } )
Animer l'élévation d'un composable
Pour animer l'élévation d'un composable, utilisez animateDpAsState
en combinaison avec
Modifier.graphicsLayer{ }
Pour des changements d'altitude ponctuels,
Modifier.shadow()
Si vous animez l'ombre, utilisez
Le modificateur Modifier.graphicsLayer{ }
est l'option la plus performante.
val mutableInteractionSource = remember { MutableInteractionSource() } val pressed = mutableInteractionSource.collectIsPressedAsState() val elevation = animateDpAsState( targetValue = if (pressed.value) { 32.dp } else { 8.dp }, label = "elevation" ) Box( modifier = Modifier .size(100.dp) .align(Alignment.Center) .graphicsLayer { this.shadowElevation = elevation.value.toPx() } .clickable(interactionSource = mutableInteractionSource, indication = null) { } .background(colorGreen) ) { }
Vous pouvez également utiliser le composable Card
et définir la propriété d'élévation sur
différentes valeurs par état.
Animer l'échelle, la traduction ou la rotation du texte
![Composable de texte disant](https://developer.android.com/static/develop/ui/compose/images/animations/animated_text.gif?authuser=6&hl=fr)
Lorsque vous animez l'échelle, la traduction ou la rotation du texte, définissez textMotion
sur TextStyle
en TextMotion.Animated
. Cela permet de fluidifier
des transitions entre
les animations de texte. Utilisez Modifier.graphicsLayer{ }
pour
pour traduire, faire pivoter
ou redimensionner le texte.
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val scale by infiniteTransition.animateFloat( initialValue = 1f, targetValue = 8f, animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "scale" ) Box(modifier = Modifier.fillMaxSize()) { Text( text = "Hello", modifier = Modifier .graphicsLayer { scaleX = scale scaleY = scale transformOrigin = TransformOrigin.Center } .align(Alignment.Center), // Text composable does not take TextMotion as a parameter. // Provide it via style argument but make sure that we are copying from current theme style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated) ) }
Animer la couleur du texte
![Les mots](https://developer.android.com/static/develop/ui/compose/images/animations/animated_text_color.gif?authuser=6&hl=fr)
Pour animer la couleur du texte, utilisez le lambda color
sur le composable BasicText
:
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val animatedColor by infiniteTransition.animateColor( initialValue = Color(0xFF60DDAD), targetValue = Color(0xFF4285F4), animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "color" ) BasicText( text = "Hello Compose", color = { animatedColor }, // ... )
Passer d'un type de contenu à un autre
![Fond vert disant](https://developer.android.com/static/develop/ui/compose/images/animations/animated_content_slower.gif?authuser=6&hl=fr)
Utilisez AnimatedContent
pour créer des animations entre différents composables, si vous
si vous souhaitez simplement un fondu standard entre les composables, utilisez Crossfade
.
var state by remember { mutableStateOf(UiState.Loading) } AnimatedContent( state, transitionSpec = { fadeIn( animationSpec = tween(3000) ) togetherWith fadeOut(animationSpec = tween(3000)) }, modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { state = when (state) { UiState.Loading -> UiState.Loaded UiState.Loaded -> UiState.Error UiState.Error -> UiState.Loading } }, label = "Animated Content" ) { targetState -> when (targetState) { UiState.Loading -> { LoadingScreen() } UiState.Loaded -> { LoadedScreen() } UiState.Error -> { ErrorScreen() } } }
AnimatedContent
peut être personnalisé pour afficher différents types d'entrées et
et de sortie. Pour en savoir plus, consultez la documentation
AnimatedContent
ou lisez cet article de blog sur
AnimatedContent
Lancer une animation pendant la navigation vers différentes destinations
![Deux composables, l'un vert indiquant "Atterrissage" et l'autre bleu, "Détail", qui s'animent en faisant glisser le composable Détail sur le composable de renvoi.](https://developer.android.com/static/develop/ui/compose/images/animations/navigation_compose_animation.gif?authuser=6&hl=fr)
Pour animer des transitions entre les composables lorsque vous utilisez la
l'artefact navigation-compose, spécifier les enterTransition
et
exitTransition
sur un composable. Vous pouvez également définir
l'animation par défaut comme
utilisé pour toutes les destinations du niveau supérieur NavHost
:
val navController = rememberNavController() NavHost( navController = navController, startDestination = "landing", enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None } ) { composable("landing") { ScreenLanding( // ... ) } composable( "detail/{photoUrl}", arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }), enterTransition = { fadeIn( animationSpec = tween( 300, easing = LinearEasing ) ) + slideIntoContainer( animationSpec = tween(300, easing = EaseIn), towards = AnimatedContentTransitionScope.SlideDirection.Start ) }, exitTransition = { fadeOut( animationSpec = tween( 300, easing = LinearEasing ) ) + slideOutOfContainer( animationSpec = tween(300, easing = EaseOut), towards = AnimatedContentTransitionScope.SlideDirection.End ) } ) { backStackEntry -> ScreenDetails( // ... ) } }
Plusieurs types de transitions d'entrée et de sortie s'appliquent différents effets sur le contenu entrant et sortant, consultez la documentation.
Répéter une animation
![Un fond vert qui se transforme en arrière-plan bleu, infiniment grâce à une animation entre les deux couleurs.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=6&hl=fr)
Utiliser rememberInfiniteTransition
avec un infiniteRepeatable
animationSpec
pour répéter l'animation en continu. Remplacer RepeatModes
par
spécifier la manière
dont elle doit aller et venir.
Utilisez finiteRepeatable
pour répéter un certain nombre de fois.
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Green, targetValue = Color.Blue, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(color) } ) { // your composable here }
Démarrer une animation au lancement d'un composable
LaunchedEffect
s'exécute lorsqu'un composable entre dans la composition. Ça commence
une animation au lancement d'un composable, vous pouvez l'utiliser pour déclencher l'animation
un changement d'état. En utilisant Animatable
avec la méthode animateTo
pour démarrer le
Animation au lancement:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Créer des animations séquentielles
![Quatre cercles avec des flèches vertes qui s'animent l'un après l'autre.](https://developer.android.com/static/develop/ui/compose/images/animations/multiple_properties_sequential.gif?authuser=6&hl=fr)
Utilisez les API de coroutine Animatable
pour effectuer des opérations séquentielles ou simultanées
des animations. Appeler animateTo
sur Animatable
l'un après l'autre
chaque animation, d'attendre la fin des animations précédentes avant de continuer .
En effet, il s'agit d'une fonction de suspension.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
Créer des animations simultanées
![Trois cercles avec des flèches vertes s'animent chacun, tous ensemble en même temps.](https://developer.android.com/static/develop/ui/compose/images/animations/multiple_properties.gif?authuser=6&hl=fr)
Utilisez les API de coroutine (Animatable#animateTo()
ou animate
) ou
L'API Transition
pour réaliser des animations simultanées. Si vous utilisez plusieurs
lance des fonctions dans un contexte de coroutine, il lance les animations en même temps
temps:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Vous pouvez utiliser l'API updateTransition
afin d'utiliser le même état pour générer
de nombreuses animations de propriétés en même temps. L'exemple ci-dessous anime
deux propriétés contrôlées par un changement d'état, rect
et borderWidth
:
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "transition") val rect by transition.animateRect(label = "rect") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "borderWidth") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
Optimiser les performances des animations
Les animations dans Compose peuvent entraîner des problèmes de performances. Cela est dû à la nature ce qu'est une animation: déplacer ou changer rapidement les pixels à l'écran, image par image pour créer l'illusion du mouvement.
Examinez les différentes phases de Compose: la composition, la mise en page et le dessin. Si votre animation modifie la phase de mise en page. Tous les composables concernés doivent donc et redessiner ou remettre en page. Si votre animation se produit lors de la phase de dessin, c'est par sera plus performante que si vous exécutiez l'animation dans la mise en page. car il aurait moins de travail à faire dans l'ensemble.
Pour que votre application effectue le moins de tâches possible lors de l'animation, choisissez le lambda
la version d'un Modifier
dans la mesure du possible. Cette action ignore la recomposition et effectue
l'animation en dehors de la phase de composition. Sinon, utilisez
Modifier.graphicsLayer{ }
, car ce modificateur s'exécute toujours dans le dessin
à chaque phase. Pour en savoir plus à ce sujet, consultez la section Différer les lectures dans
la documentation sur les performances.
Modifier la chronologie de l'animation
Par défaut, Compose utilise des animations de printemps pour la plupart des animations. Springs ou
basées sur les lois de la physique, vous paraîtront plus naturelles. Ils peuvent également être interrompus
elles prennent en compte la vitesse actuelle de l'objet, plutôt qu'une durée fixe.
Si vous souhaitez ignorer la valeur par défaut, toutes les API d'animation présentées ci-dessus
définir un animationSpec
pour personnaliser l'exécution d'une animation,
si vous souhaitez qu'il s'exécute
sur une certaine durée ou soit plus dynamique.
Voici un récapitulatif des différentes options de animationSpec
:
spring
: animation basée sur la physique, définie par défaut pour toutes les animations. Toi peut modifier la raideur ou le taux d'amortissement pour obtenir une animation différente l'aspect général.tween
(abréviation de between): animation basée sur la durée, s'anime entre deux valeurs avec une fonctionEasing
.keyframes
: spécification permettant de spécifier des valeurs à certains points clés d'une de l'animation.repeatable
: spécification basée sur la durée qui s'exécute un certain nombre de fois. spécifié parRepeatMode
.infiniteRepeatable
: spécification basée sur la durée qui s'exécute indéfiniment.snap
: se fixe instantanément à la valeur de fin, sans animation.
![Saisissez votre texte alternatif ici](https://developer.android.com/static/develop/ui/compose/images/animations/animated_spec_set.gif?authuser=6&hl=fr)
Lisez la documentation complète pour en savoir plus sur animationSpecs.
Ressources supplémentaires
Pour voir d'autres exemples d'animations amusantes dans Compose, consultez les ressources suivantes:
- 5 animations rapides dans Compose
- Faire avancer une méduse dans Compose
- Personnaliser
AnimatedContent
dans Compose - Présentation des fonctions de lissage de vitesse dans Compose