Dostosuj animacje

Wiele interfejsów API animacji zwykle akceptuje parametry służące do dostosowywania ich działania.

Dostosuj animacje za pomocą parametru AnimationSpec

Większość interfejsów API animacji umożliwia programistom dostosowywanie specyfikacji animacji za pomocą opcjonalnego parametru AnimationSpec.

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

Istnieją różne rodzaje AnimationSpec przeznaczone do tworzenia różnych rodzajów animacji.

Utwórz animację opartą na fizyce za pomocą funkcji spring

spring tworzy opartą na fizyce animację między wartością początkową a końcową. Omawia on 2 parametry: dampingRatio i stiffness.

dampingRatio określa sprężystość sprężyny. Wartość domyślna to Spring.DampingRatioNoBouncy.

Rysunek 1. Ustawienie różnych współczynników tłumienia sprężyn.

stiffness określa, jak szybko sprężyna powinna przesuwać się w kierunku wartości końcowej. Wartość domyślna to Spring.StiffnessMedium.

Rysunek 2. Ustawianie różnych sztywności sprężyn

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

spring może sprawniej obsługiwać przerwy niż typy AnimationSpec oparte na czasie trwania, ponieważ gwarantuje ciągłość prędkości, gdy wartość docelowa zmienia się w obrębie animacji. Parametr spring jest używany jako domyślna wartość AnimationSpec w wielu interfejsach API animacji, takich jak animate*AsState i updateTransition.

Jeśli np. zastosujesz do tej animacji konfigurację spring, która jest wywoływana przez dotyk użytkownika, podczas przerywania animacji w trakcie jej działania zauważysz, że użycie tween nie reaguje tak płynnie jak użycie spring.

Rysunek 3. Ustawianie specyfikacji tween i spring na potrzeby animacji i ich przerywanie.

Animuj między wartościami początkowymi i końcowymi za pomocą krzywej wygładzania za pomocą funkcji tween

Funkcja tween animuje się między wartościami początkowymi i końcowymi na określonym obszarze durationMillis za pomocą krzywej wygładzania. tween jest skrótem od słowa pomiędzy między dwiema wartościami.

Możesz też określić delayMillis, by opóźnić rozpoczęcie animacji.

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

Więcej informacji znajdziesz w sekcji Wygładzanie.

Przeprowadzanie animacji do określonych wartości w określonych momentach za pomocą funkcji keyframes

keyframes animuje się na podstawie wartości zrzutu określonych w różnych sygnaturach czasowych w czasie trwania animacji. W danym momencie wartość animacji będzie interpolowana między 2 wartościami klatek kluczowych. Dla każdej z tych klatek kluczowych można określić wygładzanie, aby określić krzywą interpolacji.

Opcjonalne jest określenie wartości 0 ms i czasu trwania. Jeśli nie określisz tych wartości, domyślnie zostaną użyte wartości początkowe i końcowe animacji.

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

Powtórz animację w narzędziu repeatable

repeatable uruchamia animację opartą na czasie trwania (np. tween lub keyframes) wielokrotnie, aż osiągnie określoną liczbę iteracji. Możesz przekazać parametr repeatMode, by określić, czy animacja ma się powtarzać, zaczynając od początku (RepeatMode.Restart) czy od końca (RepeatMode.Reverse).

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

Powtarzaj animację w nieskończoność z użyciem funkcji infiniteRepeatable

Funkcja infiniteRepeatable jest podobna do repeatable, ale powtarza się przez nieskończoną liczbę powtórzeń.

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

W testach korzystających z ComposeTestRule animacje korzystające z infiniteRepeatable nie są uruchamiane. Komponent zostanie wyrenderowany z wykorzystaniem wartości początkowej każdej animowanej wartości.

Natychmiast przyciągaj do wartości końcowej za pomocą funkcji snap

snap to specjalne AnimationSpec, które natychmiast przełącza wartość na wartość końcową. Możesz wybrać delayMillis, by opóźnić rozpoczęcie animacji.

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

Ustawianie niestandardowej funkcji wygładzania

Operacje AnimationSpec zależne od czasu trwania (np. tween lub keyframes) używają Easing do korygowania ułamka animacji. Dzięki temu animacja może przyśpieszyć i zwolnić tempo, a nie stały ruch. Ułamek to wartość z zakresu od 0 (początek) do 1,0 (koniec) wskazująca bieżący punkt animacji.

Wygładzanie jest w rzeczywistości funkcją, która przyjmuje wartość ułamkową z zakresu od 0 do 1,0 i zwraca liczbę zmiennoprzecinkową. Zwrócona wartość może znajdować się poza granicami i reprezentować zbyt wiele linii tekstu lub linii. Niestandardowe wygładzanie można utworzyć tak jak poniżej.

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

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

Tworzenie ma kilka wbudowanych funkcji Easing, które sprawdzają się w większości przypadków. Więcej informacji o tym, jak łatwo jest użyć w zależności od scenariusza, znajdziesz w artykule Szybkość – interfejs Material Design.

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearEasing
  • LinearEasing
  • CubicBezierEasing
  • Pokaż więcej

Animuj niestandardowe typy danych, konwertując dane na format AnimationVector i z powrotem

Większość interfejsów API animacji w komponowaniu obsługuje domyślnie wartości Float, Color, Dp i inne podstawowe typy danych jako wartości animacji, ale czasem trzeba animować inne typy danych, w tym te niestandardowe. Podczas animacji każda wartość animowana jest reprezentowana jako AnimationVector. Wartość jest konwertowana na AnimationVector i na odwrót przez odpowiedni element TwoWayConverter, aby główny system animacji mógł je obsługiwać równomiernie. Na przykład Int jest przedstawiany jako AnimationVector1D, który zawiera jedną wartość zmiennoprzecinkową. TwoWayConverter dla elementu Int wygląda tak:

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

Color to zasadniczo zestaw 4 wartości: czerwonego, zielonego, niebieskiego i alfa, więc Color jest konwertowany do typu AnimationVector4D, który zawiera 4 wartości zmiennoprzecinkowe. W ten sposób każdy typ danych używany w animacjach jest konwertowany na AnimationVector1D, AnimationVector2D, AnimationVector3D lub AnimationVector4D w zależności od ich wymiarów. Dzięki temu można niezależnie animować różne komponenty obiektu, każdy z własnym śledzeniem prędkości. Dostęp do wbudowanych konwerterów podstawowych typów danych można uzyskać za pomocą konwerterów takich jak Color.VectorConverter czy Dp.VectorConverter.

Aby dodać obsługę nowego typu danych jako wartości animowanej, możesz utworzyć własny obiekt TwoWayConverter i przekazać go do API. Możesz np. użyć funkcji animateValueAsState, aby animować niestandardowy typ danych w ten sposób:

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

Oto lista wbudowanych komponentów VectorConverter: