Dostosuj animacje

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

Dostosowywanie animacji za pomocą parametru AnimationSpec

Większość interfejsów API do animacji 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"
)

Do tworzenia różnych rodzajów animacji służą różne rodzaje AnimationSpec.

Tworzenie animacji opartej 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ężystość ma być duża. 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 ma się zbliżać do wartości końcowej. Wartością domyślną jest Spring.StiffnessMedium.

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

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

Typ spring może płynniej obsługiwać przerwy niż typy AnimationSpec oparte na czasie trwania, ponieważ gwarantuje ciągłość prędkości podczas zmiany wartości docelowej w animacjach. spring jest używana jako domyślna specyfikacja animacji 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 uruchamiana przez dotyk użytkownika, i przerwiemy animację w trakcie jej odtwarzania, zauważymy, że użycie tween nie powoduje tak płynnej reakcji jak użycie spring.

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

Animacja między wartościami początkową i końcową z krzywą wykładniczą z parametrem tween

tween animuje przejście między wartościami początkową i końcową w określonym zakresie durationMillis za pomocą wygładzonej krzywej. tween to skrót od słowa „between” (pomiędzy), ponieważ pomiędzy 2 wartościami.

Możesz też ustawić wartość 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 Zwolnienie.

Animacja do określonych wartości w określonym czasie za pomocą funkcji keyframes

keyframes animuje się na podstawie wartości migawkowych określonych w różnych punktach czasowych w czasie trwania animacji. W każdej chwili wartość animacji będzie interpolowana między 2 wartościami klatki kluczowej. W przypadku każdego z tych klawiszy płynności możesz określić krzywą interpolacji.

Wartości w 0 ms i w czasie trwania są opcjonalne. Jeśli nie określisz tych wartości, zostaną one domyślnie ustawione na wartości początkową i końcową 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"
)

Powtarzanie animacji za pomocą 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, 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
    ),
    label = "repeatable spec"
)

Powtarzanie animacji w nieskończoność za pomocą infiniteRepeatable

Funkcja infiniteRepeatable jest podobna do funkcji repeatable, ale powtarza się w nieskończonej liczbie iteracji.

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

W testach korzystających z ComposeTestRule animacje korzystające z infiniteRepeatable nie są uruchamiane. Składnik zostanie wyrenderowany, używając wartości początkowej każdej animowanej wartości.

Natychmiastowe zablokowanie wartości końcowej za pomocą snap

snap to specjalny AnimationSpec, który natychmiast przełącza wartość na wartość końcową. Aby opóźnić rozpoczęcie animacji, możesz ustawić wartość delayMillis.

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ą funkcji Easing do dostosowywania ułamka animacji. Dzięki temu wartość animacji może przyspieszać i zwalniać, zamiast poruszać się z równą prędkością. Ułamek to wartość z zakresu od 0 (początek) do 1,0 (koniec), która wskazuje bieżący punkt animacji.

Spłaszczenie jest w istocie funkcją, która przyjmuje wartość ułamkową z zakresu od 0 do 1,0 i zwraca liczbę zmiennoprzecinkową. Zwrócona wartość może wykraczać poza granice, co oznacza przekroczenie lub niedociągnięcie celu. Niestandardowe wygładzanie możesz 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"
    )
    // ……
}

Compose udostępnia kilka wbudowanych funkcji Easing, które obejmują większość przypadków użycia. Więcej informacji o tym, jaką metodę wygaszania należy zastosować w danym scenariuszu, znajdziesz w artykule Szybkość – Material Design.

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

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

Większość interfejsów API animacji w Compose obsługuje domyślnie wartości animacji Float, Color, Dp i inne podstawowe typy danych, ale czasami trzeba animować inne typy danych, w tym niestandardowe. Podczas animacji każda wartość animacji jest reprezentowana jako AnimationVector. Wartość jest konwertowana na AnimationVector i na odwrót za pomocą odpowiedniego TwoWayConverter, aby system animacji mógł je obsługiwać w sposób jednolity. Na przykład Int jest reprezentowane jako AnimationVector1D, które 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 podstawie zestaw 4 wartości: czerwony, zielony, niebieski i alfa, więc Color jest konwertowane na AnimationVector4D, które 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, z własnym śledzeniem prędkości. Domyślne konwertery dla podstawowych typów danych można wywołać 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łasną wartość TwoWayConverter i przekazać ją do interfejsu API. Możesz na przykład 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)
            }
        ),
        label = "size"
    )
}

Na liście znajdują się niektóre wbudowane VectorConverter: