Compose ha molti meccanismi di animazione integrati e può essere difficile scegliere quale. Di seguito è riportato un elenco di casi d'uso comuni delle animazioni. Per informazioni più dettagliate sull'insieme completo di diverse opzioni API a tua disposizione, consulta la documentazione completa di Compose Animation.
Animazione delle 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.
Animazione di comparsa / sparizione
Utilizza AnimatedVisibility
per nascondere o mostrare un componibile. I bambini all'interno di AnimatedVisibility
possono utilizzare Modifier.animateEnterExit()
per il proprio passaggio 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 appare e scompare. Per ulteriori informazioni, leggi la documentazione completa.
Un'altra opzione per animare la visibilità di un 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 il fatto che il composable rimane nella composizione e continua a occupare lo spazio in cui è disposto. Ciò potrebbe causare lo screen reader e altri meccanismi di accessibilità a considerare ancora l'elemento sullo schermo. D'altra parte, AnimatedVisibility
rimuove
l'elemento dalla composizione.
Anima colore sfondo
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Questa opzione è più efficiente rispetto all'utilizzo di Modifier.background()
.
Modifier.background()
è accettabile per un'impostazione di colore una tantum, ma quando si anima un colore nel tempo, questo potrebbe causare più ricostruzioni del necessario.
Per animare infinitamente il colore di sfondo, consulta la sezione sulla ripetizione di un'animazione.
Animazione delle dimensioni di un componibile
Compose ti consente di animare le dimensioni dei composabili in diversi modi. Utilizza
animateContentSize()
per le animazioni tra le modifiche delle dimensioni composable.
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 delle dimensioni.
Anima la posizione del composable
Per animare la posizione di un composable, utilizza Modifier.offset{ }
combinato 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 composabili non vengano disegnati sopra o sotto altri composabili durante l'animazione della posizione o delle dimensioni, utilizza Modifier.layout{ }
. Questo
modificatore propaga le modifiche di dimensioni e posizione al contenitore principale, che poi influisce su
gli altri elementi secondari.
Ad esempio, se stai spostando un Box
all'interno di un Column
e gli altri elementi figli devono spostarsi quando si sposta il Box
, includi le informazioni sull'offset con Modifier.layout{ }
come segue:
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) ) }
Animare il padding di un composable
Per animare il padding di un composable, utilizza animateDpAsState
combinato 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 } )
Animazione dell'elevazione di un componibile
Per animare l'elevazione di un composable, utilizza animateDpAsState
combinato con
Modifier.graphicsLayer{ }
. Per le variazioni di altitudine una tantum, utilizza
Modifier.shadow()
. Se stai animando l'ombra, l'opzione più performante è l'utilizzo del modificatore Modifier.graphicsLayer{ }
.
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 composable Card
e imposta la proprietà altezza su valori diversi per stato.
Animare la scala, la traslazione o la rotazione del testo
Quando animi la scala, la traslazione o la rotazione del testo, imposta il parametro textMotion
su TextStyle
su TextMotion.Animated
. Ciò garantisce transizioni più fluide tra le animazioni di testo. Usa Modifier.graphicsLayer{ }
per tradurre, ruotare o ridimensionare 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) ) }
Colore del testo animato
Per animare il colore del testo, utilizza la 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 contenuto all'altro
Utilizza AnimatedContent
per animare il passaggio da un composable all'altro. Se vuoi semplicemente 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 di uscita. Per ulteriori informazioni, leggi la documentazione su
AnimatedContent
o leggi questo post del blog su
AnimatedContent
.
Animare durante la navigazione verso destinazioni diverse
Per animare le transizioni tra composabili quando utilizzi l'elemento
navigation-compose, specifica enterTransition
e
exitTransition
in un composable. Puoi anche impostare l'animazione predefinita da utilizzare per tutte le destinazioni a livello superiore 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 di 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 infiniteRepeatable
animationSpec
per ripetere continuamente l'animazione. Modifica RepeatModes
per
specificare come deve andare avanti e indietro.
Utilizza finiteRepeatable
per ripetere un numero predefinito 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 }
Avvia un'animazione all'avvio di un componibile
LaunchedEffect
viene eseguito quando un componibile entra nella composizione. Avvia un'animazione all'avvio di un composable, che puoi utilizzare per gestire la modifica dello stato dell'animazione. Utilizza 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 Animatable
coroutine per eseguire animazioni sequenziali o simultanee. Se chiami animateTo
su Animatable
una dopo l'altra, ogni animazione attende il termine delle animazioni precedenti prima di procedere .
Questo perché si tratta di 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)) }
Crea animazioni simultanee
Utilizza le API Coroutine (Animatable#animateTo()
o animate
) o
l'API Transition
per ottenere animazioni in parallelo. Se utilizzi più funzioni di lancio 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 gestire contemporaneamente molte animazioni di proprietà diverse. L'esempio seguente anima due proprietà controllate da una modifica di 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 le prestazioni delle animazioni
Le animazioni in Compose possono causare problemi di prestazioni. Questo è dovuto alla natura dell'animazione: spostare o modificare rapidamente i pixel sullo schermo, fotogramma per fotogramma per creare l'illusione del movimento.
Considera le diverse fasi di Componi: composizione, layout e disegno. Se la tua animazione modifica la fase di layout, richiede il relayout e il ridisegni di tutti i composabili interessati. Se l'animazione avviene nella fase di disegno, per impostazione predefinita avrà prestazioni migliori rispetto a quando eseguirai l'animazione nella fase di layout, in quanto richiederebbe meno lavoro da eseguire nel complesso.
Per assicurarti che la tua app faccia il meno possibile durante l'animazione, scegli la versione lambda di un Modifier
, se possibile. In questo modo viene saltata la ricompozione ed eseguita
l'animazione al di fuori della fase di composizione, altrimenti utilizza
Modifier.graphicsLayer{ }
, poiché questo modificatore viene eseguito sempre nella fase di disegno. Per ulteriori informazioni, consulta la sezione Rimandare le letture nella documentazione sul rendimento.
Modificare la durata dell'animazione
Per impostazione predefinita, Compose utilizza le animazioni elastiche per la maggior parte delle animazioni. Le molle o le animazioni basate sulla fisica sono più naturali. Sono inoltre interrompibili poiché tengono conto della velocità corrente 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 il funzionamento di un'animazione, ad esempio per eseguirla per una determinata durata o renderla più elastica.
Di seguito è riportato un riepilogo delle diverse opzioni animationSpec
:
spring
: animazione basata sulla fisica, predefinita per tutte le animazioni. Puoi modificare la rigidità o il rapporto di smorzamento per ottenere un aspetto diverso dell'animazione.tween
(abbreviazione di tra): animazione basata sulla durata, anima tra due valori con una funzioneEasing
.keyframes
: specifica per specificare i valori in determinati punti chiave in un'animazione.repeatable
: specifica basata sulla durata che viene eseguita un determinato numero di volte, specificato daRepeatMode
.infiniteRepeatable
: specifica basata sulla durata eseguita all'infinito.snap
: si aggancia istantaneamente al valore finale senza alcuna animazione.
Leggi la documentazione completa per ulteriori informazioni su animationSpecs.
Risorse aggiuntive
Per altri esempi di animazioni divertenti in Compose, dai un'occhiata a quanto segue:
- 5 animazioni rapide in Scrittura
- Far muovere Jellyfish in Scrivi
- Personalizzazione di
AnimatedContent
in Compose - Introduzione alle funzioni di transizione in Compose