Beispiel für eine erweiterte Animation: Gesten

Bei der Arbeit mit Google Workspace for Education mit Touch-Ereignissen und Animationen, im Vergleich zur Animationen zu erstellen. Zunächst müssen wir möglicherweise eine laufende Animation unterbrechen, wenn Touch-Ereignisse beginnen, da eine Nutzerinteraktion die höchste Priorität haben sollte.

Im folgenden Beispiel verwenden wir ein Animatable, um den Versatz der Position Kreiskomponente. Touch-Ereignisse werden mit dem pointerInput Modifikator. Wird ein neues Tippereignis erkannt, wird animateTo aufgerufen, um die Versatzwert auf die Tippposition setzen. Während der Animation kann ein Tippereignis stattfinden. In diesem Fall unterbricht animateTo die laufende Animation und startet die Animation an die neue Zielposition an, während die Geschwindigkeit der unterbrochene Animation.

@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())

Ein weiteres häufiges Muster ist die Synchronisierung von Animationswerten mit Werten. von Touch-Ereignissen wie Drag. Im Beispiel unten sehen wir Schließen" als Modifier implementiert werden (anstatt das Tag SwipeToDismiss zusammensetzbar). Der horizontale Versatz des Elements wird als Animatable Diese API hat eine Eigenschaft, die für die Bewegungsanimation nützlich ist. Das kann durch Touch-Ereignisse und die Animation geändert werden. Wenn wir eine Touchdown-Ereignis: Animatable stoppen wir mit der stop-Methode, sodass alle wird die laufende Animation abgefangen.

Während eines Drag-Ereignisses verwenden wir snapTo, um den Animatable-Wert mit dem Wert, der aus Touch-Ereignissen berechnet wurde. Mit der E-Mail-Funktion „Schreiben“ VelocityTracker, um Drag-Ereignisse aufzuzeichnen und die Geschwindigkeit zu berechnen. Die Geschwindigkeit kann für die Fling-Animation direkt in animateDecay eingespeist werden. Wenn wir eine Position um den Offset-Wert auf die ursprüngliche Position zurückzuversetzen, geben wir den Ziel-Offset an. 0f-Wert mit der animateTo-Methode.

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) }
}