Hay varios aspectos que se deben tener en cuenta cuando trabajamos con animaciones y eventos táctiles, en comparación con los casos en que trabajamos solo con animaciones. En primer lugar, es posible que debamos interrumpir una animación en curso cuando comienzan los eventos táctiles, ya que la interacción del usuario debe tener la prioridad más alta.
En el siguiente ejemplo, usamos un Animatable
para representar la posición de desplazamiento de un componente circular. Los eventos táctiles se procesan con el modificador pointerInput
. Cuando detectamos un nuevo evento de toque, llamamos a animateTo
para animar el valor de desplazamiento a la posición del toque. Un evento de toque también puede ocurrir durante la animación y, en ese caso, animateTo
interrumpe la animación en curso y comienza la animación a la nueva posición objetivo, a la vez que se mantiene la velocidad de la animación interrumpida.
@Composable fun Gesture() { val offset = remember { Animatable(Offset(0f, 0f), Offset.VectorConverter) } Box( modifier = Modifier .fillMaxSize() .pointerInput(Unit) { coroutineScope { while (true) { // Detect a tap event and obtain its position. awaitPointerEventScope { val position = awaitFirstDown().position launch { // Animate to the tap position. offset.animateTo(position) } } } } } ) { Circle(modifier = Modifier.offset { offset.value.toIntOffset() }) } } private fun Offset.toIntOffset() = IntOffset(x.roundToInt(), y.roundToInt())
Otro patrón frecuente es tener que sincronizar valores de animación con valores provenientes de eventos táctiles, como los arrastres. En el siguiente ejemplo, vemos que "deslizar para descartar" se implementa como un Modifier
(en lugar de usar el SwipeToDismiss
componible). El desplazamiento horizontal del elemento se representa como un Animatable
. Esta API tiene una característica útil en la animación de gestos. Su valor puede cambiar mediante eventos táctiles y la animación. Cuando recibimos un evento de toque, detenemos el Animatable
mediante el método stop
para que se intercepte cualquier animación en curso.
Durante un evento de arrastre, usamos snapTo
para actualizar el valor Animatable
con el valor calculado de los eventos táctiles. Para la navegación, Compose proporciona VelocityTracker
a fin de registrar eventos de arrastre y calcular la velocidad. Se puede transmitir la velocidad directamente a animateDecay
para la animación de navegación. Cuando queremos deslizar el valor de desplazamiento de vuelta a la posición original, especificamos el valor de desplazamiento objetivo de 0f
con el método animateTo
.
fun Modifier.swipeToDismiss( onDismissed: () -> Unit ): Modifier = composed { val offsetX = remember { Animatable(0f) } pointerInput(Unit) { // Used to calculate fling decay. val decay = splineBasedDecay<Float>(this) // Use suspend functions for touch events and the Animatable. coroutineScope { while (true) { val velocityTracker = VelocityTracker() // Stop any ongoing animation. offsetX.stop() awaitPointerEventScope { // Detect a touch down event. val pointerId = awaitFirstDown().id horizontalDrag(pointerId) { change -> // Update the animation value with touch events. launch { offsetX.snapTo( offsetX.value + change.positionChange().x ) } velocityTracker.addPosition( change.uptimeMillis, change.position ) } } // No longer receiving touch events. Prepare the animation. val velocity = velocityTracker.calculateVelocity().x val targetOffsetX = decay.calculateTargetValue( offsetX.value, velocity ) // The animation stops when it reaches the bounds. offsetX.updateBounds( lowerBound = -size.width.toFloat(), upperBound = size.width.toFloat() ) launch { if (targetOffsetX.absoluteValue <= size.width) { // Not enough velocity; Slide back. offsetX.animateTo( targetValue = 0f, initialVelocity = velocity ) } else { // The element was swiped away. offsetX.animateDecay(velocity, decay) onDismissed() } } } } } .offset { IntOffset(offsetX.value.roundToInt(), 0) } }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Animaciones basadas en el valor
- Arrastrar, deslizar y lanzar
- Información sobre los gestos