Animationen anpassen

Viele der Animations-APIs akzeptieren in der Regel Parameter zum Anpassen ihres Verhaltens.

Animationen mit dem Parameter AnimationSpec anpassen

Bei den meisten Animations-APIs können Entwickler Animationsspezifikationen mit einem optionalen AnimationSpec-Parameter 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, mit denen sich unterschiedliche Arten von Animationen erstellen lassen.

Physikbasierte Animationen mit spring erstellen

Mit spring wird eine physikbasierte Animation zwischen Start- und Endwerten erstellt. Sie verwendet zwei Parameter: dampingRatio und stiffness.

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

Abbildung 1. Unterschiedliche Federdämpfungsverhältnisse festlegen.

stiffness definiert, wie schnell sich die Feder dem Endwert nähern soll. Der Standardwert ist Spring.StiffnessMedium.

Abbildung 2: Sie können die Federsteifigkeit unterschiedlich einstellen.

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

spring kann Unterbrechungen besser verarbeiten als dauerbasierte AnimationSpec-Typen, da die Kontinuität der Geschwindigkeit garantiert wird, wenn sich der Zielwert während der Animationen ändert. spring wird von vielen Animations-APIs wie animate*AsState und updateTransition als Standard-AnimationSpec verwendet.

Wenn wir beispielsweise eine spring-Konfiguration auf die folgende Animation anwenden, die durch Berührung des Nutzers ausgelöst wird, und die Animation unterbrechen, während sie läuft, sehen Sie, dass die Reaktion bei Verwendung von tween nicht so flüssig ist wie bei Verwendung von spring.

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

Animation zwischen Start- und Endwerten mit einer Easing-Kurve mit tween

tween animiert zwischen Start- und Endwerten über die angegebene durationMillis mithilfe einer Easing-Kurve. tween ist die Kurzform für das Wort „zwischen“, da es zwischen zwei Werten steht.

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 können Sie Animationen für bestimmte Werte zu bestimmten Zeitpunkten erstellen.

keyframes wird basierend auf den Snapshot-Werten animiert, die zu verschiedenen Zeitstempeln während der Animation angegeben werden. Zu jedem Zeitpunkt wird der Animationswert zwischen zwei Keyframe-Werten interpoliert. Für jeden dieser Keyframes kann ein Easing-Wert 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 können Sie Animationen zwischen Keyframes flüssig gestalten.

Wenn Sie eine Animation erstellen möchten, die beim Übergang zwischen Werten einer glatten Kurve folgt, können Sie anstelle von keyframes-Animationsspezifikationen 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 splinebasierte Keyframes weichere Übergänge zwischen Punkten, da sie Bezierkurven verwenden, um Elemente flüssig zu animieren. Diese Spezifikation ist für eine voreingestellte Animation nützlich. Wenn Sie jedoch mit benutzerdefinierten Punkten arbeiten, ist es besser, Federn zu verwenden, um eine ähnliche Glätte 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. Mit dem Parameter repeatMode können Sie angeben, ob die Animation wiederholt werden soll, indem sie von Anfang (RepeatMode.Restart) oder vom Ende (RepeatMode.Reverse) beginnt.

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 ist 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"
)

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

Sofort zum Endwert springen mit snap

snap ist ein spezieller AnimationSpec, der 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

Dauerbasierte AnimationSpec-Vorgänge (z. B. tween oder keyframes) verwenden Easing, um den Bruchteil einer Animation anzupassen. So kann der animierte Wert beschleunigen und verlangsamen, anstatt sich mit einer konstanten Geschwindigkeit zu bewegen. „Fraction“ ist ein Wert zwischen 0 (Anfang) und 1,0 (Ende), der den aktuellen Punkt in der Animation angibt.

Easing ist eine Funktion, die einen Bruchwert zwischen 0 und 1,0 akzeptiert und einen Gleitkommawert zurückgibt. Der zurückgegebene Wert kann außerhalb der Grenze liegen, um eine Überschreitung oder Unterschreitung darzustellen. Ein benutzerdefiniertes Easing 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 dazu, welche Easing-Funktion Sie je nach Szenario verwenden sollten, finden Sie unter Geschwindigkeit – Material Design.

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

Benutzerdefinierte Datentypen animieren, indem sie in AnimationVector konvertiert werden und umgekehrt

Die meisten Compose-Animations-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. Während der Animation wird jeder animierte Wert als AnimationVector dargestellt. Der Wert wird durch ein entsprechendes TwoWayConverter in ein AnimationVector konvertiert und umgekehrt, damit das Core-Animationssystem sie einheitlich verarbeiten kann. Ein Int wird beispielsweise als AnimationVector1D dargestellt, das 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 Grunde ein Satz aus 4 Werten: Rot, Grün, Blau und Alpha. Daher wird Color in ein AnimationVector4D umgewandelt, das 4 Gleitkommawerte enthält. Auf diese Weise wird jeder in Animationen verwendete Datentyp je nach Dimensionalität in AnimationVector1D, AnimationVector2D, AnimationVector3D oder AnimationVector4D konvertiert. So können verschiedene Komponenten des Objekts unabhängig voneinander animiert werden, jeweils mit eigener Geschwindigkeitsmessung. Auf integrierte Konverter für einfache Datentypen kann über Konverter wie Color.VectorConverter oder Dp.VectorConverter zugegriffen werden.

Wenn Sie Unterstützung für einen neuen Datentyp als animierenden Wert hinzufügen möchten, können Sie ein eigenes 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 VectorConverters: