Para personalizar la forma en que se ejecuta la animación de transición de elementos compartidos, hay algunos parámetros que se pueden usar para cambiar la forma en que se transfieren los elementos compartidos.
Especificación de animación
Para cambiar la especificación de animación que se usa para el movimiento de tamaño y posición, puedes especificar un parámetro boundsTransform
diferente en Modifier.sharedElement()
.
Esto proporciona la posición inicial de Rect
y la posición de destino de Rect
.
Por ejemplo, para que el texto del ejemplo anterior se mueva con un movimiento de arco, especifica el parámetro boundsTransform
para usar una especificación keyframes
:
val textBoundsTransform = BoundsTransform { initialBounds, targetBounds -> keyframes { durationMillis = boundsAnimationDurationMillis initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing targetBounds at boundsAnimationDurationMillis } } Text( "Cupcake", fontSize = 28.sp, modifier = Modifier.sharedBounds( rememberSharedContentState(key = "title"), animatedVisibilityScope = animatedVisibilityScope, boundsTransform = textBoundsTransform ) )
Puedes usar cualquier AnimationSpec
. En este ejemplo, se usa una especificación keyframes
.
Modo de cambio de tamaño
Cuando animas entre dos límites compartidos, puedes establecer el parámetro resizeMode
en RemeasureToBounds
o ScaleToBounds
. Este parámetro determina cómo el elemento compartido realiza la transición entre los dos estados. ScaleToBounds
primero mide el diseño secundario con las restricciones de visualización anticipada (o objetivo). Luego, el diseño estable del elemento secundario se ajusta para que se ajuste a los límites compartidos.
Se puede considerar que ScaleToBounds
es una "escala gráfica" entre los estados.
Mientras que RemeasureToBounds
vuelve a medir y a diseñar el diseño secundario de sharedBounds
con restricciones fijas animadas según el tamaño objetivo. La re-medición se activa por el cambio de tamaño de los límites, que podría ser cada fotograma.
Para los elementos componibles Text
, se recomienda ScaleToBounds
, ya que evitará el rediseño y el reflujo de texto en diferentes líneas. Para límites que tienen diferentes relaciones de aspecto y si deseas una continuidad fluida entre los dos elementos compartidos, se recomienda RemeasureToBounds
.
La diferencia entre los dos modos de cambio de tamaño se puede ver en los siguientes ejemplos:
|
|
---|---|
Omitir al diseño final
De forma predeterminada, cuando se realiza una transición entre dos diseños, el tamaño del diseño se anima entre su estado inicial y final. Este puede ser un comportamiento no deseado cuando se anima contenido, como texto.
En el siguiente ejemplo, se ilustra el texto de descripción "Lorem Ipsum" que ingresa a la pantalla de dos maneras diferentes. En el primer ejemplo, el texto se vuelve a ajustar a medida que entra a medida que el contenedor aumenta de tamaño. En el segundo ejemplo, el texto no se vuelve a ajustar a medida que crece. Agregar Modifier.skipToLookaheadSize()
evita el reflujo a medida que crece.
Sin Modifier.skipToLookahead(): Observa cómo se vuelve a fluir el texto "Lorem Ipsum". |
Modifier.skipToLookahead(): Observa que el texto "Lorem Ipsum" mantiene su estado final al comienzo de la animación. |
---|---|
Clips y superposiciones
Un concepto importante cuando se crean elementos compartidos en Compose es que, para que se compartan entre diferentes elementos componibles, la renderización del elemento componible se eleva a una superposición de capas cuando se inicia la transición a su coincidencia en el destino. El efecto de esto es que escapará de los límites del elemento superior y de sus transformaciones de capas (por ejemplo, la alfa y la escala).
Se renderizará sobre otros elementos de la IU no compartidos. Una vez que finalice la transición, el elemento se soltará de la superposición a su propio DrawScope
.
Para recortar un elemento compartido en una forma, usa la función Modifier.clip()
estándar. Colócalo después de sharedElement()
:
Image( painter = painterResource(id = R.drawable.cupcake), contentDescription = "Cupcake", modifier = Modifier .size(100.dp) .sharedElement( rememberSharedContentState(key = "image"), animatedVisibilityScope = this@AnimatedContent ) .clip(RoundedCornerShape(16.dp)), contentScale = ContentScale.Crop )
Si necesitas asegurarte de que un elemento compartido nunca se renderice fuera de un contenedor superior, puedes configurar clipInOverlayDuringTransition
en sharedElement()
. De forma predeterminada, para los límites compartidos anidados, clipInOverlayDuringTransition
usa la ruta de acceso del clip del sharedBounds()
superior.
Para admitir que los elementos específicos de la IU, como una barra inferior o un botón de acción flotante, siempre estén en la parte superior durante una transición de elementos compartidos, usa Modifier.renderInSharedTransitionScopeOverlay()
. De forma predeterminada, este modificador mantiene el contenido en la superposición durante el tiempo en que la transición compartida está activa.
Por ejemplo, en Jetsnack, el BottomAppBar
se debe colocar sobre el elemento compartido hasta que la pantalla no sea visible. Agregar el modificador al elemento componible lo mantiene elevado.
Sin |
Con |
---|---|
A veces, es posible que desees que el elemento componible no compartido se analice y permanezca sobre los otros elementos componibles antes de la transición. En esos casos, usa renderInSharedTransitionScopeOverlay().animateEnterExit()
para animar el elemento componible mientras se ejecuta la transición de elementos compartidos:
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
En el caso poco frecuente de que quieras que tu elemento compartido no se renderice en una superposición, puedes establecer el renderInOverlayDuringTransition
en sharedElement()
como falso.
Notifica a los diseños hermanos sobre los cambios en el tamaño del elemento compartido
De forma predeterminada, sharedBounds()
y sharedElement()
no notifican al contenedor superior ningún cambio de tamaño a medida que el diseño realiza transiciones.
Para propagar los cambios de tamaño al contenedor superior a medida que realiza la transición, cambia el parámetro placeHolderSize
a PlaceHolderSize.animatedSize
. De esta manera, el elemento se agranda o se reduce. Todos los demás elementos del diseño responden al cambio.
|
(observa cómo los otros elementos de la lista se mueven hacia abajo en respuesta al crecimiento de un elemento) |
---|---|