Dostosuj animacje

Wiele interfejsów API animacji często akceptuje parametry na potrzeby dostosowywania swojego 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 obiektu AnimationSpec do tworzenia animacji.

Twórz oparte na fizyce animacje w spring

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

dampingRatio określa dynamikę wiosny. Wartością domyślną jest Spring.DampingRatioNoBouncy.

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

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

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

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

spring radzi sobie z przerwami płynniej niż w przypadku typów AnimationSpec zależnych od czasu trwania, ponieważ gwarantuje ciągłość prędkości, gdy wartość docelowa zmienia się w animacjach. Obiekt spring jest używany jako domyślna specyfikacja animacji w wielu interfejsach API animacji, takich jak animate*AsState i updateTransition.

Jeśli na przykład zastosujesz konfigurację spring do tej animacji zależnej od dotknięcia użytkownika, to podczas przerywania jej odtwarzania w trakcie jej odtwarzania zauważysz, że użycie tween nie reaguje tak płynnie jak spring.

Rysunek 3. Ustawienie specyfikacji tween i spring dla animacji i jej przerwanie.

Animuj między wartością początkową a końcową za pomocą krzywej wygładzania: tween

tween animuje wartość między wartością początkową a końcową w obrębie określonej wartości durationMillis za pomocą krzywej wygładzania. tween to skrót od słowa między - i oznacza między dwiema wartościami.

Możesz też określić delayMillis, aby opóźnić początek animacji.

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

Więcej informacji znajdziesz w sekcji Wygładzanie.

Używaj funkcji keyframes, aby w określonych momentach wyświetlać animacje pod kątem określonych wartości

keyframes animuje się na podstawie wartości zrzutu określonych w różnych sygnaturach czasowych podczas trwania animacji. Wartość animacji zostanie w każdej chwili 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.

Określenie wartości z zakresu 0 ms i czasu trwania jest opcjonalne. Jeśli nie określisz tych wartości, domyślnie będą stosowane 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
    }
)

Powtarzanie animacji przy użyciu funkcji repeatable

repeatable 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ć, 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ść przy użyciu funkcji infiniteRepeatable

infiniteRepeatable jest jak repeatable, ale powtarza się w nieskończonej liczbie iteracji.

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

W testach z użyciem parametru ComposeTestRule animacje korzystające z metody infiniteRepeatable nie są uruchamiane. Komponent zostanie wyrenderowany na podstawie początkowej wartości każdej wartości animowanej.

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

snap to specjalny element 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)
)

Ustawianie niestandardowej funkcji wygładzania

Operacje AnimationSpec oparte na czasie trwania (np. tween lub keyframes) używają Easing do dostosowania ułamka animacji. Dzięki temu wartości animacji mogą przyspieszać i spowalniać ruch, 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ą od 0 do 1,0 i zwraca liczbę zmiennoprzecinkową. Zwrócona wartość może wykraczać poza granicę i reprezentować zbyt długi lub zbyt krótki okres. Możesz utworzyć niestandardowe wygładzanie, jak pokazano 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
        )
    )
    // ……
}

Tworzenie wiadomości udostępnia kilka wbudowanych funkcji Easing, które mają zastosowanie w większości przypadków. Więcej informacji o tym, jakich funkcji wygładzania wybrać w zależności od sytuacji, znajdziesz w artykule Szybkość – styl Material Design.

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

Animuj niestandardowe typy danych, konwertując je z i do AnimationVector

Większość interfejsów API tworzenia animacji domyślnie obsługuje jako wartości animacji Float, Color, Dp i inne podstawowe typy danych, ale czasami trzeba animować inne typy danych, w tym Twoje niestandardowe. Podczas animacji każda wartość animacji jest reprezentowana przez parametr AnimationVector. Wartość jest konwertowana na AnimationVector i odwrotnie przez odpowiedni element TwoWayConverter, aby główny system animacji mógł obsługiwać je równomiernie. Na przykład Int jest przedstawiony 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: czerwonego, zielonego, niebieskiego i alfa, więc funkcja 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 wymiarów. Dzięki temu różne komponenty obiektu mogą być animowane niezależnie, każdy z osobnym ś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 animacji, możesz utworzyć własny obiekt TwoWayConverter i przesłać go do interfejsu API. Za pomocą animateValueAsState możesz na przykład 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)
            }
        )
    )
}

Ta lista zawiera niektóre wbudowane komponenty VectorConverter: