Dostosuj animacje

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

Dostosowywanie animacji 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),
    label = "alpha"
)

Istnieją różne rodzaje AnimationSpec, które umożliwiają tworzenie 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: dampingRatiostiffness.

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ężyn.

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óżnej sztywności sprężyny.

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

spring lepiej radzi sobie z przerwami niż typy oparte na czasie trwania,AnimationSpec ponieważ gwarantuje ciągłość prędkości, gdy wartość docelowa zmienia się w trakcie animacji. spring jest używany jako domyślny element AnimationSpec przez wiele interfejsów API animacji, takich jak animate*AsStateupdateTransition.

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

Rysunek 3. Ustawianie specyfikacji tween i spring dla animacji oraz przerywanie jej.

Animacja między wartościami początkową i końcową z krzywą łagodzenia o wartości tween

tween animuje wartości od początkowej do końcowej w określonym czasiedurationMillis, używając krzywej wygładzania. tween to skrót od słowa „between” (między), ponieważ funkcja ta sprawdza, czy wartość znajduje się między dwiema innymi wartościami.

Możesz też użyć wartości 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 Easing (Zmiękczanie).

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

keyframes animuje się na podstawie wartości stanu określonych w różnych znacznikach czasu w okresie trwania animacji. W dowolnym momencie wartość animacji będzie interpolowana między dwiema wartościami klatek kluczowych. W przypadku każdego z tych klatek kluczowych można określić wygładzanie, aby wyznaczyć krzywą interpolacji.

Określanie wartości w momencie 0 ms i w momencie trwania jest opcjonalne. Jeśli nie określisz tych wartości, domyślnie będą to odpowiednio 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łynne animowanie między klatkami kluczowymi za pomocą funkcji keyframesWithSplines

Aby utworzyć animację, która podczas przechodzenia między wartościami porusza się po gładkiej krzywej, możesz użyć specyfikacji animacji keyframesWithSplines zamiast keyframes.

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 splajnach są szczególnie przydatne w przypadku dwuwymiarowego ruchu elementów na ekranie.

Poniższe filmy pokazują różnice między keyframeskeyframesWithSpline przy tym samym zestawie współrzędnych x i y, po których powinien poruszać się okrąg.

keyframes keyframesWithSplines

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

Powtarzanie animacji za pomocą repeatable

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

Funkcja infiniteRepeatable działa podobnie jak 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 z użyciem ComposeTestRule animacje z użyciem infiniteRepeatable nie są uruchamiane. Komponent zostanie wyrenderowany przy użyciu wartości początkowej każdej animowanej wartości.

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

snap to specjalny 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 łagodzenia

Operacje oparte na czasie trwania AnimationSpec (np. 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.

Funkcja łagodzenia to funkcja, która przyjmuje wartość ułamkową z zakresu od 0 do 1,0 i zwraca liczbę zmiennoprzecinkową. Zwracana wartość może wykraczać poza granicę, aby odzwierciedlać przekroczenie lub niedobór. Niestandardowe wygładzanie można utworzyć za pomocą kodu poniżej.

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

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

Funkcja Pisanie ma kilka wbudowanych funkcji Easing, które obejmują większość przypadków użycia. Więcej informacji o tym, jakiego rodzaju wygładzania używać w zależności od sytuacji, znajdziesz w artykule Szybkość – Material Design.

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

Animowanie niestandardowych typów danych przez konwertowanie na AnimationVector i z AnimationVector

Większość interfejsów API animacji w Compose 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 obsługiwać je 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 w zasadzie zestaw 4 wartości: czerwonej, zielonej, niebieskiej i alfa, więc Color jest przekształcana w AnimationVector4D, która 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 jego wymiarowości. Dzięki temu różne komponenty obiektu mogą być animowane niezależnie od siebie, a każdy z nich ma własne śledzenie prędkości. Wbudowane konwertery podstawowych typów danych są dostępne za pomocą konwerterów takich jak Color.VectorConverter lub Dp.VectorConverter.

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

Poniżej znajdziesz listę wbudowanych VectorConverter: