Compose ha molti meccanismi di animazione integrati e può essere complicato sapere quale scegliere. Di seguito è riportato un elenco di casi d'uso comuni relativi alle animazioni. Per informazioni più dettagliate sul set completo delle diverse opzioni API disponibili, leggi la documentazione completa su Compose Animation.
Animazione delle proprietà componibili comuni
Compose fornisce pratiche API che consentono di risolvere molti casi d'uso comuni di animazione. Questa sezione illustra come animare le proprietà comuni di un componibile.
Animazione che appare / scompare
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 un'avvertenza: la componibile rimane nella composizione e continua a occupare lo spazio in cui è disposto. Di conseguenza, gli screen reader e altri meccanismi di accessibilità potrebbero comunque tenere in considerazione l'elemento sullo schermo. AnimatedVisibility
alla fine 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 one-shot, ma l'animazione di un colore nel tempo potrebbe causare più ricomposizioni del necessario.
Per animare all'infinito 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 componibili in diversi modi. Utilizza animateContentSize()
per le animazioni tra le modifiche delle dimensioni componibili.
Ad esempio, se hai un riquadro che contiene 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 dovrebbero avvenire le modifiche alle dimensioni.
Animazione posizione del componibile
Per animare la posizione di un componibile, 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 } )
Per assicurarti che i componenti componibili non vengano disegnati sopra o sotto altri elementi componibili quando si anima la posizione o la dimensione, utilizza Modifier.layout{ }
. Questo
modificatore propaga le modifiche di dimensioni e posizione all'elemento padre, con ripercussioni sugli altri elementi figlio.
Ad esempio, se stai spostando un Box
all'interno di un Column
e gli altri elementi secondari
devono spostarsi quando Box
si sposta, includi le informazioni di 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) ) }
Animazione della spaziatura interna di un elemento componibile
Per animare la spaziatura interna di un componibile, 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 componibile, utilizza animateDpAsState
combinato con
Modifier.graphicsLayer{ }
. Per i dislivelli occasionali, utilizza
Modifier.shadow()
. Se stai animando l'ombra, il tasto di modifica
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 l'elemento componibile Card
e imposta la proprietà di elevazione su
valori diversi in base allo stato.
Anima la scala, la traduzione o la rotazione del testo
Quando si anima 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) ) }
Anima colore testo
Per animare il colore del testo, utilizza la funzione lambda color
nell'elemento componibile 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 eseguire l'animazione tra diversi elementi componibili. Se invece vuoi solo una dissolvenza standard tra elementi componibili, 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 leggi questo post del blog su
AnimatedContent
.
Crea animazioni durante la navigazione verso destinazioni diverse
Per animare le transizioni tra gli elementi componibili quando utilizzi l'artefatto navigation-compose, specifica enterTransition
e
exitTransition
su un componibile. 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 ulteriori informazioni, 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.
Usa finiteRepeatable
per ripetere un determinato numero 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 componibile. Puoi utilizzare questa opzione per favorire il cambiamento 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 coroutine Animatable
per eseguire animazioni sequenziali o simultanee. Se chiami animateTo
su Animatable
uno dopo l'altro, ogni animazione attende il completamento delle animazioni precedenti prima di procedere .
Il motivo è che 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 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 generare 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 } }
Ottimizza le prestazioni delle animazioni
Le animazioni in Compose possono causare problemi di prestazioni. Ciò è dovuto alla natura di un'animazione: spostare o modificare rapidamente pixel sullo schermo, fotogramma per fotogramma per creare l'illusione di movimento.
Considera le diverse fasi di Scrittura: composizione, layout e disegno. Se l'animazione cambia la fase di layout, è necessario che tutti gli elementi componibili interessati ridiseggano e ridiseggano. Se l'animazione si verifica 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 l'app esegua il meno possibile durante l'animazione, se possibile scegli la versione lambda
di un elemento Modifier
. In questo modo viene saltata la ricomposizione e viene eseguita
l'animazione al di fuori della fase di composizione. In caso contrario, utilizza
Modifier.graphicsLayer{ }
, poiché questo modificatore viene sempre eseguito nella fase
di disegno. Per ulteriori informazioni, consulta la sezione Differire le letture nella documentazione sulle prestazioni.
Modificare la durata dell'animazione
Per impostazione predefinita, la funzionalità Scrivi utilizza le animazioni primaverili per la maggior parte delle animazioni. Le molle, o animazioni basate sulla fisica, sembrano più naturali. Sono inoltre interrompibili poiché tengono conto
della velocità attuale dell'oggetto, anziché di un tempo fisso.
Se vuoi eseguire l'override dell'impostazione predefinita, tutte le API di animazione mostrate sopra
hanno la possibilità di impostare un animationSpec
per personalizzare la modalità di esecuzione di un'animazione,
che venga eseguita durante una certa durata o più scorrevole.
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'animazione diversa.tween
(abbreviazione di tra): animazione basata sulla durata, si 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 eseguita un certo numero di volte, specificata 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 Compose
- Spostare le meduse in Compose
- Personalizzazione di
AnimatedContent
in Compose - Accesso alle funzioni di easing in Compose