Animationen anpassen

Viele der Animation APIs akzeptieren häufig Parameter zum Anpassen ihres Verhaltens.

Animationen mit dem Parameter AnimationSpec anpassen

Bei den meisten Animation APIs können Entwickler Animationsspezifikationen mit einem optionalen Parameter AnimationSpec anpassen.

val alpha: Float by animateFloatAsState(
    targetValue = if (enabled) 1f else 0.5f,
    // Configure the animation duration and easing.
    animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing),
    label = "alpha"
)

Es gibt verschiedene Arten von AnimationSpec zum Erstellen verschiedener Arten von Animationen.

Physikbasierte Animation mit spring erstellen

spring erstellt eine physikbasierte Animation zwischen Start- und Endwerten. Dazu werden zwei Parameter verwendet: dampingRatio und stiffness.

dampingRatio definiert, wie stark die Feder schwingen soll. Der Standardwert ist Spring.DampingRatioNoBouncy.

Abbildung 1. Verschiedene Dämpfungsverhältnisse für die Feder festlegen.

stiffness definiert, wie schnell sich die Feder auf den Endwert zubewegen soll. Der Standardwert ist Spring.StiffnessMedium.

Abbildung 2. Verschiedene Steifigkeiten für die Feder festlegen.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioHighBouncy,
        stiffness = Spring.StiffnessMedium
    ),
    label = "spring spec"
)

spring kann Unterbrechungen reibungsloser verarbeiten als zeitbasierte AnimationSpec-Typen, da die Kontinuität der Geschwindigkeit garantiert wird, wenn sich der Zielwert während der Animation ändert. spring wird von vielen Animation APIs als Standard-AnimationSpec verwendet, z. B. von animate*AsState und updateTransition.

Wenn wir beispielsweise eine spring-Konfiguration auf die folgende Animation anwenden, die durch eine Nutzerberührung ausgelöst wird, können Sie sehen, dass die Reaktion bei einer Unterbrechung der Animation mit tween nicht so reibungslos ist wie mit spring.

Abbildung 3. tween- und spring-Spezifikationen für die Animation festlegen und die Animation unterbrechen.

Mit tween zwischen Start- und Endwerten animieren

tween animiert zwischen Start- und Endwerten über die angegebene durationMillis mit einer Easing-Kurve. tween ist eine Abkürzung für „between“ (zwischen), da die Animation zwischen zwei Werten verläuft.

Sie können auch delayMillis angeben, um den Start der Animation zu verzögern.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = tween(
        durationMillis = 300,
        delayMillis = 50,
        easing = LinearOutSlowInEasing
    ),
    label = "tween delay"
)

Weitere Informationen finden Sie unter Easing.

Mit keyframes zu bestimmten Zeiten bestimmte Werte animieren

keyframes animiert basierend auf den Snapshot-Werten, die zu verschiedenen Zeitstempeln während der Animation angegeben wurden. Zu jedem Zeitpunkt wird der Animationswert zwischen zwei Keyframe-Werten interpoliert. Für jedes dieser Keyframes kann Easing angegeben werden, um die Interpolationskurve zu bestimmen.

Es ist optional, die Werte bei 0 ms und bei der Dauer anzugeben. Wenn Sie diese Werte nicht angeben, werden standardmäßig die Start- und Endwerte der Animation verwendet.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = keyframes {
        durationMillis = 375
        0.0f at 0 using LinearOutSlowInEasing // for 0-15 ms
        0.2f at 15 using FastOutLinearInEasing // for 15-75 ms
        0.4f at 75 // ms
        0.4f at 225 // ms
    },
    label = "keyframe"
)

Mit keyframesWithSplines reibungslos zwischen Keyframes animieren

Wenn Sie eine Animation erstellen möchten, die beim Übergang zwischen Werten einer reibungslosen Kurve folgt, können Sie anstelle von keyframes die Animation specs keyframesWithSplines verwenden.

val offset by animateOffsetAsState(
    targetValue = Offset(300f, 300f),
    animationSpec = keyframesWithSpline {
        durationMillis = 6000
        Offset(0f, 0f) at 0
        Offset(150f, 200f) atFraction 0.5f
        Offset(0f, 100f) atFraction 0.7f
    }
)

Spline-basierte Keyframes sind besonders nützlich für die 2D-Bewegung von Elementen auf dem Bildschirm.

In den folgenden Videos werden die Unterschiede zwischen keyframes und keyframesWithSpline anhand derselben Menge von x- und y-Koordinaten veranschaulicht, denen ein Kreis folgen soll.

keyframes keyframesWithSplines

Wie Sie sehen, bieten Spline-basierte Keyframes reibungslosere Übergänge zwischen Punkten, da sie Bézierkurven verwenden, um reibungslos zwischen Elementen zu animieren. Diese Spezifikation ist nützlich für eine voreingestellte Animation. Wenn Sie jedoch mit nutzergesteuerten Punkten arbeiten, ist es besser, Federn zu verwenden, um eine ähnliche Reibungslosigkeit zwischen den Punkten zu erzielen, da diese unterbrechbar sind.

Animation mit repeatable wiederholen

repeatable führt eine zeitbasierte Animation (z. B. tween oder keyframes) wiederholt aus, bis die angegebene Anzahl von Wiederholungen erreicht ist. Sie können den Parameter repeatMode übergeben, um anzugeben, ob die Animation wiederholt werden soll, indem sie von Anfang an (RepeatMode.Restart) oder vom Ende aus (RepeatMode.Reverse) gestartet wird.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = repeatable(
        iterations = 3,
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    ),
    label = "repeatable spec"
)

Animation mit infiniteRepeatable unendlich oft wiederholen

infiniteRepeatable funktioniert wie repeatable, wird aber unendlich oft wiederholt.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    ),
    label = "infinite repeatable"
)

In Tests mit ComposeTestRule, werden Animationen mit infiniteRepeatable nicht ausgeführt. Die Komponente wird mit dem Anfangswert jedes animierten Werts gerendert.

Mit snap sofort zum Endwert springen

snap ist eine spezielle AnimationSpec, die den Wert sofort auf den Endwert ändert. Sie können delayMillis angeben, um den Start der Animation zu verzögern.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = snap(delayMillis = 50),
    label = "snap spec"
)

Benutzerdefinierte Easing-Funktion festlegen

Zeitbasierte AnimationSpec-Vorgänge (z. B. tween oder keyframes) verwenden Easing, um den Anteil einer Animation anzupassen. Dadurch kann der animierte Wert beschleunigt und verlangsamt werden, anstatt sich mit einer konstanten Geschwindigkeit zu bewegen. Der Anteil ist ein Wert zwischen 0 (Start) und 1,0 (Ende), der den aktuellen Punkt in der Animation angibt.

Easing ist eine Funktion, die einen Anteilswert zwischen 0 und 1,0 verwendet und einen Gleitkommawert zurückgibt. Der zurückgegebene Wert kann außerhalb der Grenze liegen, um ein Überschwingen oder Unterschwingen darzustellen. Eine benutzerdefinierte Easing-Funktion kann wie im folgenden Code erstellt werden.

val CustomEasing = Easing { fraction -> fraction * fraction }

@Composable
fun EasingUsage() {
    val value by animateFloatAsState(
        targetValue = 1f,
        animationSpec = tween(
            durationMillis = 300,
            easing = CustomEasing
        ),
        label = "custom easing"
    )
    // ……
}

Compose bietet mehrere integrierte Easing-Funktionen, die die meisten Anwendungsfälle abdecken. Weitere Informationen zur Verwendung von Easing je nach Szenario finden Sie unter Geschwindigkeit – Material Design.

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearEasing
  • LinearEasing
  • CubicBezierEasing
  • Mehr anzeigen

Benutzerdefinierte Datentypen animieren, indem sie in AnimationVector konvertiert werden

Die meisten Compose Animation APIs unterstützen standardmäßig Float, Color, Dp und andere grundlegende Datentypen als Animationswerte. Manchmal müssen Sie jedoch andere Datentypen animieren, einschließlich Ihrer benutzerdefinierten Datentypen. Während der Animation wird jeder animierte Wert als AnimationVector dargestellt. Der Wert wird mit einem entsprechenden TwoWayConverter in einen AnimationVector und umgekehrt konvertiert, sodass das Kernanimationssystem sie einheitlich verarbeiten kann. Ein Int wird beispielsweise als AnimationVector1D dargestellt, der einen einzelnen Gleitkommawert enthält. TwoWayConverter für Int sieht so aus:

val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
    TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })

Color ist im Wesentlichen eine Menge von vier Werten: Rot, Grün, Blau und Alpha. Daher wird Color in einen AnimationVector4D konvertiert, der vier Gleitkommawerte enthält. Auf diese Weise wird jeder in Animationen verwendete Datentyp je nach Dimensionalität in AnimationVector1D, AnimationVector2D, AnimationVector3D oder AnimationVector4D konvertiert. Dadurch können verschiedene Komponenten des Objekts unabhängig voneinander animiert werden, jeweils mit eigener Geschwindigkeitsverfolgung. Auf integrierte Konverter für grundlegende Datentypen kann mit Konvertern wie Color.VectorConverter oder Dp.VectorConverter zugegriffen werden.

Wenn Sie Unterstützung für einen neuen Datentyp als Animationswert hinzufügen möchten, können Sie einen eigenen TwoWayConverter erstellen und der API zur Verfügung stellen. Mit animateValueAsState können Sie beispielsweise Ihren benutzerdefinierten Datentyp so animieren:

data class MySize(val width: Dp, val height: Dp)

@Composable
fun MyAnimation(targetSize: MySize) {
    val animSize: MySize by animateValueAsState(
        targetSize,
        TwoWayConverter(
            convertToVector = { size: MySize ->
                // Extract a float value from each of the `Dp` fields.
                AnimationVector2D(size.width.value, size.height.value)
            },
            convertFromVector = { vector: AnimationVector2D ->
                MySize(vector.v1.dp, vector.v2.dp)
            }
        ),
        label = "size"
    )
}

Die folgende Liste enthält einige integrierte VectorConverter: