Compose tiene muchos mecanismos de animación incorporados y puede resultar abrumador saber cuál elegir. A continuación, se muestra una lista de casos de uso comunes de animaciones. Para información más detallada sobre el conjunto completo de diferentes opciones de API disponibles lee la documentación completa de Compose Animation.
Cómo animar propiedades comunes de componibilidad
Compose proporciona APIs convenientes que te permiten resolver problemas comunes casos de uso de animación. En esta sección, se muestra cómo animar comunes propiedades de un elemento componible.
Cómo animar la aparición y la desaparición
![Elemento componible verde que se muestra y se oculta](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_column.gif?authuser=7&hl=es)
Usa AnimatedVisibility
para ocultar o mostrar un elemento componible. Niños en el interior
AnimatedVisibility
puede usar Modifier.animateEnterExit()
para su propia entrada
o salir de la transición.
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 // ... }
Los parámetros de entrada y salida de AnimatedVisibility
te permiten configurar cómo
un elemento componible se comporta cuando aparece y desaparece. Leer todo
documentación para obtener más información.
Otra opción para animar la visibilidad de un elemento componible es animar el
alfa a lo largo del tiempo con 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) ) { }
Sin embargo, cambiar la versión alfa tiene la salvedad de que el elemento componible sigue siendo
en la composición y sigue ocupando el espacio en el que está distribuida. Esta
podría hacer que los lectores de pantalla y otros mecanismos de accesibilidad sigan teniendo en cuenta
el elemento en pantalla. Por otro lado, AnimatedVisibility
eventualmente quita.
el elemento de la composición.
![Cómo animar el alfa de un elemento componible](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_alpha.gif?authuser=7&hl=es)
Cómo animar el color de fondo
![Es componible con el color de fondo que cambia con el tiempo como una animación, en la que los colores se desvanecen.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=7&hl=es)
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Esta opción tiene un mejor rendimiento que usar Modifier.background()
.
Modifier.background()
es aceptable para una configuración de color de una sola toma, pero cuando
animar un color con el paso del tiempo, esto podría provocar más recomposiciones que
necesario.
Para animar infinitamente el color de fondo, consulta cómo repetir una animación .
Cómo animar el tamaño de un elemento componible
![Elemento componible verde que anima su cambio de tamaño sin problemas.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_content_size.gif?authuser=7&hl=es)
Compose te permite animar el tamaño de los elementos componibles de varias maneras. Usa
animateContentSize()
para animaciones entre cambios de tamaño de elementos componibles
Por ejemplo, si tienes un cuadro con texto que se puede expandir de uno
varias líneas, puedes usar Modifier.animateContentSize()
para lograr una representación
transición:
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 } ) { }
También puedes usar AnimatedContent
, con un SizeTransform
para describir
cómo deben realizarse los cambios de tamaño.
Cómo animar la posición de un elemento componible
![Elemento componible verde que se anima suavemente hacia abajo y hacia la derecha](https://developer.android.com/static/develop/ui/compose/images/animations/animated_offset.gif?authuser=7&hl=es)
Para animar la posición de un elemento componible, usa Modifier.offset{ }
combinado 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 } )
Si deseas asegurarte de que los elementos componibles no se dibujen sobre o debajo de otros
los elementos componibles cuando se anima la posición o el tamaño, usa Modifier.layout{ }
. Esta
el modificador propaga los cambios de tamaño y posición al elemento superior, que luego afecta
con otros niños.
Por ejemplo, si mueves un Box
dentro de un Column
y el otro elemento secundario
cuando se mueve el Box
, incluye la información de desplazamiento con
Modifier.layout{ }
de la siguiente manera:
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) ) }
![2 cuadros con el segundo cuadro que anima su posición X e Y, y el tercer cuadro responde moviéndose también en Y cantidad.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_layout_modifier.gif?authuser=7&hl=es)
Modifier.layout{ }
Cómo animar el padding de un elemento componible
![El elemento componible verde se vuelve más pequeño y grande cuando se hace clic, y se anima el padding.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_padding.gif?authuser=7&hl=es)
Para animar el padding de un elemento componible, usa animateDpAsState
combinado 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 } )
Cómo animar la elevación de un elemento componible
Para animar la elevación de un elemento componible, usa animateDpAsState
combinado con
Modifier.graphicsLayer{ }
Para realizar cambios de elevación únicos, usa
Modifier.shadow()
Si deseas animar la sombra, usa
El modificador Modifier.graphicsLayer{ }
es la opción con mejor rendimiento.
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) ) { }
Como alternativa, usa el elemento componible Card
y establece la propiedad de elevación en
diferentes valores por estado.
Cómo animar la escala, la traslación o la rotación de texto
![Texto componible que dice](https://developer.android.com/static/develop/ui/compose/images/animations/animated_text.gif?authuser=7&hl=es)
Cuando animes la escala, la traslación o la rotación del texto, establece el textMotion
parámetro de TextStyle
en TextMotion.Animated
. Esto garantiza una mayor fluidez
transiciones entre animaciones de texto. Usa Modifier.graphicsLayer{ }
para
traducir, rotar o ajustar el texto.
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) ) }
Cómo animar el color de texto
![Aparece la frase](https://developer.android.com/static/develop/ui/compose/images/animations/animated_text_color.gif?authuser=7&hl=es)
Para animar el color del texto, usa la expresión lambda color
en el elemento BasicText
componible:
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 }, // ... )
Cómo alternar entre diferentes tipos de contenido
![Pantalla verde que dice](https://developer.android.com/static/develop/ui/compose/images/animations/animated_content_slower.gif?authuser=7&hl=es)
Usa AnimatedContent
para animar entre diferentes elementos componibles, si
solo quieres un atenuación estándar entre elementos componibles, usa 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
se puede personalizar para mostrar muchos tipos diferentes de entradas y
para salir de las transiciones. Para obtener más información, consulta la documentación sobre
AnimatedContent
o lee esta entrada de blog en
AnimatedContent
Realiza animaciones mientras navegas a diferentes destinos
![Dos elementos componibles, uno verde que dice Landing y otro azul que dice Detalle. Para animarlos, desliza el elemento componible de detalles sobre el elemento componible de destino.](https://developer.android.com/static/develop/ui/compose/images/animations/navigation_compose_animation.gif?authuser=7&hl=es)
Para animar transiciones entre elementos componibles cuando se usa el
navigation-compose, especifica el enterTransition
y
exitTransition
en un elemento componible También puedes establecer la animación predeterminada
Se usa para todos los destinos en el nivel superior 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( // ... ) } }
Existen muchos tipos diferentes de transiciones de entrada y salida que se aplican diferentes efectos para el contenido entrante y saliente, consulta el documentación para obtener más información.
Cómo repetir una animación
![Un fondo verde que se transforma en un fondo azul, infinitamente animando entre los dos colores.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=7&hl=es)
Usa rememberInfiniteTransition
con un infiniteRepeatable
.
animationSpec
para repetir la animación de forma continua. Cambiar RepeatModes
a
y especificar cómo debe ir y volver.
Usa finiteRepeatable
para repetir una cantidad determinada de veces.
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 }
Cómo iniciar una animación cuando se inicia un elemento componible
LaunchedEffect
se ejecuta cuando un elemento componible ingresa en la composición. Comienza
una animación cuando se inicia un elemento componible, puedes usarla para impulsar la animación
cambio de estado. Usa Animatable
con el método animateTo
para iniciar la
animación al iniciar:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Cómo crear animaciones secuenciales
![Cuatro círculos con flechas verdes que se mueven entre cada uno y se animan una por una.](https://developer.android.com/static/develop/ui/compose/images/animations/multiple_properties_sequential.gif?authuser=7&hl=es)
Usa las APIs de corrutinas de Animatable
para realizar tareas secuenciales o simultáneas.
animaciones. Llamar a animateTo
en Animatable
uno después de las otras causas
cada animación a esperar a que finalicen las animaciones anteriores antes de continuar .
Esto se debe a que es una función de suspensión.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
Cómo crear animaciones simultáneas
![Tres círculos con flechas verdes que se animan a cada uno y se animan todos al mismo tiempo.](https://developer.android.com/static/develop/ui/compose/images/animations/multiple_properties.gif?authuser=7&hl=es)
Usa las APIs de corrutinas (Animatable#animateTo()
o animate
).
la API de Transition
para lograr animaciones simultáneas. Si usas varios
inicia funciones en un contexto de corrutinas, lanza las animaciones al mismo tiempo
hora:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Podrías usar la API de updateTransition
para usar el mismo estado para impulsar
muchas animaciones de propiedades diferentes al mismo tiempo. En el siguiente ejemplo, se muestra una animación
dos propiedades controladas por un cambio de estado, rect
y 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 } }
Cómo optimizar el rendimiento de las animaciones
Las animaciones en Compose pueden causar problemas de rendimiento. Esto se debe a la naturaleza de una animación: mover o cambiar píxeles en la pantalla rápidamente, fotograma por fotograma para crear la ilusión de movimiento.
Considera las diferentes fases de Compose: composición, diseño y dibujo. Si Tu animación cambia la fase de diseño, requiere que todos los elementos componibles afectados rediseñar y volver a dibujar. Si la animación ocurre en la fase de dibujo, es por tendrá un mejor rendimiento de forma predeterminada que si ejecutaras la animación en el diseño ya que tendría menos trabajo para hacer en general.
Para asegurarte de que tu app haga lo menos posible durante la animación, elige la expresión lambda.
versión de un objeto Modifier
cuando sea posible. Esto omite la recomposición y realiza
la animación fuera de la fase de composición, de lo contrario, usa
Modifier.graphicsLayer{ }
, ya que este modificador siempre se ejecuta en el dibujo
y la fase de desarrollo. Para obtener más información sobre este tema, consulta la sección aplaza las lecturas en
la documentación de rendimiento.
Cómo cambiar los tiempos de las animaciones
De forma predeterminada, Compose usa animaciones de resorte para la mayoría de las animaciones. Springs o
basadas en la física, se sienten más naturales. También son interrumpibles, ya que
consideran la velocidad actual del objeto, en lugar de un tiempo fijo.
Si quieres anular el valor predeterminado, todas las APIs de Animation que se mostraron anteriormente
puedes configurar un animationSpec
para personalizar cómo se ejecuta una animación
si quieres que se ejecute a lo largo
de una duración determinada o que sea más dinámica.
A continuación, se muestra un resumen de las diferentes opciones de animationSpec
:
spring
: Animación basada en la física, el valor predeterminado para todas las animaciones. Tú puede cambiar la rigidez o el valor de dampingRatio para lograr una animación diferente y la experiencia del usuario.tween
(abreviatura de between): Animación basada en la duración, animación. entre dos valores con una funciónEasing
.keyframes
: especificación para especificar valores en ciertos puntos clave de una animación.repeatable
: Es la especificación basada en la duración que se ejecuta una cierta cantidad de veces. especificadas porRepeatMode
.infiniteRepeatable
: Especificación basada en la duración que se ejecuta de forma permanente.snap
: Se ajusta al valor final al instante sin ninguna animación.
![Escribe tu texto alternativo aquí](https://developer.android.com/static/develop/ui/compose/images/animations/animated_spec_set.gif?authuser=7&hl=es)
Lee la documentación completa para obtener más información sobre animationSpecs.
Recursos adicionales
Para obtener más ejemplos de animaciones divertidas en Compose, consulta lo siguiente:
- 5 animaciones rápidas en Compose
- Cómo trasladar Jellyfish en Compose
- Cómo personalizar
AnimatedContent
en Compose - Aceleración de las funciones de aceleración en Compose