Compose ha molti meccanismi di animazione integrati e può essere difficile sapere quale scegliere. Di seguito è riportato un elenco di casi d'uso comuni per le animazioni. Per informazioni più dettagliate sull'insieme completo delle diverse opzioni API disponibili per te, leggi la documentazione completa di Compose Animation.
Animare le proprietà componibili comuni
Compose fornisce API pratiche che ti consentono di risolvere molti casi d'uso comuni per le animazioni. Questa sezione mostra come animare le proprietà comuni di un composable.
Animare la comparsa / scomparsa

Utilizza AnimatedVisibility
per nascondere o mostrare un elemento componibile. I bambini all'interno di
AnimatedVisibility
possono utilizzare Modifier.animateEnterExit()
per la propria transizione di entrata
o uscita.
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 // ... }
I parametri di entrata e uscita di AnimatedVisibility
consentono di configurare il comportamento di un componibile quando viene visualizzato e scompare. Per saperne di più, consulta la documentazione
completa.
Un'altra opzione per animare la visibilità di un elemento componibile è animare
l'alpha nel tempo utilizzando 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) ) { }
Tuttavia, la modifica dell'alpha comporta l'avvertenza che il composable rimane
nella composizione e continua a occupare lo spazio in cui è disposto. In questo modo,
gli screen reader e altri meccanismi di accessibilità potrebbero continuare a considerare
l'elemento sullo schermo. D'altra parte, AnimatedVisibility
alla fine rimuove
l'elemento dalla composizione.

Anima il colore di sfondo

val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Questa opzione offre un rendimento migliore rispetto all'utilizzo di Modifier.background()
.
Modifier.background()
è accettabile per un'impostazione di colore una tantum, ma quando
si anima un colore nel tempo, ciò potrebbe causare più ricomposizioni del
necessario.
Per animare all'infinito il colore di sfondo, consulta la sezione Ripetere un'animazione.
Animare le dimensioni di un elemento componibile

Compose ti consente di animare le dimensioni dei composable in diversi modi. Utilizza
animateContentSize()
per le animazioni tra modifiche delle dimensioni componibili.
Ad esempio, se hai una casella contenente testo che può espandersi da una a più righe, puoi utilizzare Modifier.animateContentSize()
per ottenere una transizione più fluida:
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 } ) { }
Puoi anche utilizzare AnimatedContent
, con un SizeTransform
per descrivere
come devono avvenire le modifiche alle dimensioni.
Animare la posizione del componente

Per animare la posizione di un composable, utilizza Modifier.offset{ }
in combinazione con
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 } )
Se vuoi assicurarti che i composable non vengano disegnati sopra o sotto altri
composable durante l'animazione della posizione o delle dimensioni, utilizza Modifier.layout{ }
. Questo
modificatore propaga le modifiche di dimensioni e posizione all'elemento principale, che a sua volta influisce
sugli altri elementi secondari.
Ad esempio, se sposti un Box
all'interno di un Column
e gli altri elementi secondari
devono spostarsi quando si sposta il Box
, includi le informazioni sull'offset con
Modifier.layout{ }
nel seguente modo:
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) ) }

Modifier.layout{ }
Animare il padding di un elemento componibile

Per animare il padding di un composable, utilizza animateDpAsState
in combinazione con
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 } )
Animare l'elevazione di un elemento componibile
Per animare l'elevazione di un elemento componibile, utilizza animateDpAsState
in combinazione con
Modifier.graphicsLayer{ }
. Per variazioni di elevazione una tantum, utilizza
Modifier.shadow()
. Se animi l'ombra, l'utilizzo del modificatore
Modifier.graphicsLayer{ }
è l'opzione più efficiente.
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) ) { }
In alternativa, utilizza il componente componibile Card
e imposta la proprietà di elevazione su
valori diversi per stato.
Animare la scala, la traslazione o la rotazione del testo

Quando animi la scalatura, la traslazione o la rotazione del testo, imposta il parametro textMotion
su TextStyle
su TextMotion.Animated
. In questo modo le transizioni tra le animazioni di testo sono più fluide. Utilizza Modifier.graphicsLayer{ }
per
tradurre, ruotare o scalare il testo.
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) ) }
Animare il colore del testo

Per animare il colore del testo, utilizza l'espressione lambda color
nel 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 }, // ... )
Passare da un tipo di contenuti all'altro

Utilizza AnimatedContent
per l'animazione tra diversi composable. Se vuoi solo una dissolvenza standard tra i composable, utilizza 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
può essere personalizzato per mostrare molti tipi diversi di transizioni di entrata e
uscita. Per ulteriori informazioni, leggi la documentazione su
AnimatedContent
o questo post del blog su
AnimatedContent
.
Animare durante il viaggio verso destinazioni diverse

Per animare le transizioni tra i composable quando utilizzi l'artefatto
navigation-compose, specifica enterTransition
e
exitTransition
in un composable. Puoi anche impostare l'animazione predefinita da utilizzare per tutte le destinazioni di primo livello 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( // ... ) } }
Esistono molti tipi diversi di transizioni di entrata e uscita che applicano effetti diversi ai contenuti in entrata e in uscita. Per saperne di più, consulta la documentazione.
Ripetere un'animazione

Utilizza rememberInfiniteTransition
con un infiniteRepeatable
animationSpec
per ripetere continuamente l'animazione. Modifica RepeatModes
per
specificare come deve andare avanti e indietro.
Utilizza finiteRepeatable
per ripetere un numero prestabilito di volte.
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 }
Avviare un'animazione all'avvio di un composable
LaunchedEffect
viene eseguito quando un elemento componibile entra nella composizione. Avvia
un'animazione all'avvio di un elemento componibile. Puoi utilizzare questo parametro per gestire la modifica dello stato dell'animazione. Utilizzo di Animatable
con il metodo animateTo
per avviare l'animazione all'avvio:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Creare animazioni sequenziali

Utilizza le API di coroutine Animatable
per eseguire animazioni sequenziali o simultanee. La chiamata di animateTo
su Animatable
una dopo l'altra fa sì che
ogni animazione attenda il completamento delle animazioni precedenti prima di procedere .
Questo perché è una funzione di sospensione.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
Creare animazioni simultanee

Utilizza le API coroutine (Animatable#animateTo()
o animate
) o
l'API Transition
per ottenere animazioni simultanee. Se utilizzi più
funzioni di avvio in un contesto di coroutine, le animazioni vengono avviate contemporaneamente:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Puoi utilizzare l'API updateTransition
per utilizzare lo stesso stato per animare
molte proprietà diverse contemporaneamente. L'esempio seguente anima
due proprietà controllate da una modifica dello stato, rect
e 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 } }
Ottimizzare il rendimento delle animazioni
Le animazioni in Compose possono causare problemi di prestazioni. Ciò è dovuto alla natura di un'animazione: pixel in movimento o in cambiamento sullo schermo rapidamente, fotogramma per fotogramma per creare l'illusione del movimento.
Considera le diverse fasi di Composizione: composizione, layout e disegno. Se l'animazione modifica la fase di layout, richiede che tutti i composable interessati vengano ridisposti e ridisegnati. Se l'animazione si verifica nella fase di disegno, per impostazione predefinita avrà un rendimento migliore rispetto a se la eseguissi nella fase di layout, in quanto il lavoro da svolgere complessivamente sarà inferiore.
Per assicurarti che la tua app esegua il minor numero possibile di operazioni durante l'animazione, scegli la versione lambda di un Modifier
, se possibile. In questo modo la ricomposizione viene ignorata e l'animazione viene eseguita al di fuori della fase di composizione. In caso contrario, utilizza Modifier.graphicsLayer{ }
, poiché questo modificatore viene sempre eseguito nella fase di disegno. Per saperne di più, consulta la sezione Differimento delle letture nella documentazione sul rendimento.
Modificare la durata dell'animazione
Per impostazione predefinita, Compose utilizza animazioni spring per la maggior parte delle animazioni. Le molle o
le animazioni basate sulla fisica sembrano più naturali. Inoltre, sono interrompibili perché
tengono conto della velocità attuale dell'oggetto, anziché di un tempo fisso.
Se vuoi eseguire l'override del valore predefinito, tutte le API di animazione mostrate sopra
hanno la possibilità di impostare un animationSpec
per personalizzare l'esecuzione di un'animazione,
che tu voglia che venga eseguita per una determinata durata o che sia più elastica.
Di seguito è riportato un riepilogo delle diverse opzioni di animationSpec
:
spring
: animazione basata sulla fisica, l'impostazione predefinita per tutte le animazioni. Puoi modificare la rigidità o il rapporto di smorzamento per ottenere un aspetto e un comportamento diversi dell'animazione.tween
(abbreviazione di between, tra): animazione basata sulla durata, anima tra due valori con una funzioneEasing
.keyframes
: specifica i valori in determinati punti chiave di un'animazione.repeatable
: specifica basata sulla durata che viene eseguita un determinato numero di volte, specificato daRepeatMode
.infiniteRepeatable
: specifica basata sulla durata che viene eseguita per sempre.snap
: si sposta immediatamente sul valore finale senza alcuna animazione.

Per saperne di più su animationSpecs, leggi la documentazione completa.
Risorse aggiuntive
Per altri esempi di animazioni divertenti in Compose, dai un'occhiata a quanto segue:
- 5 animazioni rapide in Scrivi
- Spostare Jellyfish in Scrivi
- Personalizzazione di
AnimatedContent
in Scrivi - Introduzione graduale alle funzioni di easing in Compose