Dostosuj animacje

Wiele interfejsów Animation API zwykle akceptuje parametry umożliwiające dostosowywanie ich działania.

Dostosowywanie animacji za pomocą parametru AnimationSpec

Większość interfejsów Animation API umożliwia deweloperom 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),
    label = "alpha"
)

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

Tworzenie animacji opartych na fizyce za pomocą spring

spring tworzy animację opartą na fizyce między wartościami początkowymi i końcowymi. Przyjmuje 2 parametry: dampingRatio i stiffness.

dampingRatio określa, jak sprężysta powinna być sprężyna. Wartością domyślną jest Spring.DampingRatioNoBouncy.

Rysunek 1. Ustawianie różnych współczynników tłumienia sprężyny.

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

Rysunek 2. Ustawianie różnych poziomów sztywności sprężyny.

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

spring może obsługiwać przerwy płynniej niż typy oparte na czasie trwania (AnimationSpec), ponieważ gwarantuje ciągłość szybkości, gdy wartość docelowa zmienia się w trakcie animacji. spring jest używany jako domyślny AnimationSpec przez wiele interfejsów API animacji, takich jak animate*AsState i updateTransition.

Jeśli na przykład zastosujemy konfigurację spring do poniższej animacji, która jest sterowana dotykiem użytkownika, to po przerwaniu animacji w trakcie jej trwania zobaczysz, że użycie tween nie reaguje tak płynnie jak użycie spring.

Rysunek 3. Ustawianie specyfikacji tween i spring dla animacji i jej przerywania.

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

tween animuje między wartościami początkowymi i końcowymi w określonym durationMillis, używając krzywej wygładzania. tween to skrót od słowa „between” (pomiędzy), ponieważ znajduje się pomiędzy 2 wartościami.

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

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

Więcej informacji znajdziesz w sekcji Wygładzanie.

Animuj do określonych wartości w określonych momentach za pomocą keyframes

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

Określenie wartości dla 0 ms i czasu trwania jest opcjonalne. 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 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"
)

Płynnie animuj między klatkami kluczowymi za pomocą keyframesWithSplines

Aby utworzyć animację, która podczas przechodzenia między wartościami podąża za gładką krzywą, zamiast specyfikacji animacji keyframes możesz użyć keyframesWithSplines.

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

Klatki kluczowe oparte na krzywych są szczególnie przydatne w przypadku ruchu elementów na ekranie w 2D.

Te filmy pokazują różnice między keyframes i keyframesWithSpline przy tym samym zestawie współrzędnych x, y, które powinien śledzić okrąg.

keyframes keyframesWithSplines

Jak widać, klatki kluczowe oparte na krzywych sklejanych zapewniają płynniejsze przejścia między punktami, ponieważ wykorzystują krzywe Beziera do płynnej animacji między elementami. Ta specyfikacja jest przydatna w przypadku gotowej animacji. Jeśli jednak pracujesz z punktami sterowanymi przez użytkownika, lepiej jest używać sprężyn, aby uzyskać podobną płynność między punktami, ponieważ można je przerywać.

Powtarzanie animacji za pomocą repeatable

repeatable wielokrotnie uruchamia animację opartą na czasie trwania (np. tween lub keyframes), aż osiągnie określoną liczbę iteracji. Możesz przekazać parametr repeatMode, aby określić, czy animacja ma się powtarzać 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
    ),
    label = "repeatable spec"
)

Powtarzanie animacji w nieskończoność za pomocą właściwości infiniteRepeatable

infiniteRepeatable działa podobnie do repeatable, ale powtarza się w nieskończoność.

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

W testach, które używają ComposeTestRule, animacje korzystające z infiniteRepeatable nie są uruchamiane. Komponent zostanie wyrenderowany z użyciem początkowej wartości każdej animowanej wartości.

Natychmiastowe przejście do wartości końcowej za pomocą snap

snap to specjalny obiekt AnimationSpec, który natychmiast przełącza wartość na wartość końcową. Możesz określić delayMillis, aby opóźnić rozpoczęcie animacji.

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

Ustawianie niestandardowej funkcji wygładzania

Operacje AnimationSpec oparte na czasie trwania (takie jak tween lub keyframes) używają Easing do dostosowywania ułamka animacji. Dzięki temu animowana wartość może przyspieszać i zwalniać, zamiast poruszać się ze stałą szybkością. Ułamek to wartość z zakresu od 0 (początek) do 1,0 (koniec) wskazująca bieżący punkt animacji.

Wygładzanie to funkcja, która przyjmuje wartość ułamkową z zakresu od 0 do 1,0 i zwraca liczbę zmiennoprzecinkową. Zwrócona wartość może wykraczać poza granicę, aby reprezentować przekroczenie lub niedociągnięcie. Niestandardową funkcję Easing można utworzyć w sposób pokazany w poniższym kodzie.

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 udostępnia kilka wbudowanych funkcji Easing, które obejmują większość przypadków użycia. Więcej informacji o tym, jakiego wygładzania użyć w zależności od scenariusza, znajdziesz w artykule Szybkość – Material Design.

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

Animowanie niestandardowych typów danych przez konwersję do i z AnimationVector

Większość interfejsów Compose Animation API domyślnie obsługuje Float, Color, Dp i inne podstawowe typy danych jako wartości animacji, ale czasami trzeba animować inne typy danych, w tym niestandardowe. Podczas animacji każda animowana wartość jest reprezentowana jako AnimationVector. Wartość jest konwertowana na AnimationVector i odwrotnie przez odpowiedni TwoWayConverter, dzięki czemu podstawowy system animacji może je obsługiwać w jednolity sposób. Na przykład Int jest reprezentowany jako AnimationVector1D, który zawiera pojedynczą wartość zmiennoprzecinkową. TwoWayConverter dla Int wygląda tak:

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

Color to zasadniczo zestaw 4 wartości: czerwonej, zielonej, niebieskiej i alfa, więc Color jest konwertowany na 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 wymiarowości. Dzięki temu różne komponenty obiektu mogą być animowane niezależnie, 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 lub Dp.VectorConverter.

Jeśli chcesz dodać obsługę nowego typu danych jako wartości animowanej, możesz utworzyć własny obiekt TwoWayConverter i przekazać go do interfejsu API. Możesz na przykład użyć 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)
            }
        ),
        label = "size"
    )
}

Lista poniżej zawiera niektóre wbudowane VectorConverter: