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

Istnieją różne rodzaje obiektu AnimationSpec do tworzenia animacji.

Twórz oparte na fizyce animacje w spring

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

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 zastosujesz konfigurację spring do tej animacji zależnej od dotknięcia użytkownika, podczas przerywania jej odtwarzania w trakcie jej odtwarzania zauważysz, że użycie tween nie reaguje tak płynnie jak przy użyciu 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 wartość początkową i końcową w obrębie określonej wartości durationMillis za pomocą krzywej wygładzania. tween to skrót od słowa „between” (pomiędzy), ponieważ pomiędzy 2 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
    ),
    label = "tween delay"
)

Więcej informacji znajdziesz w sekcji Wygładzanie.

Animacja do określonych wartości w określonych momentach 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żna określić krzywą interpolacji.

Określanie wartości w 0 ms i w czasie 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 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

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
    ),
    label = "infinite repeatable"
)

W testach z użyciem parametru ComposeTestRule animacje korzystające z metody 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.

Złagodnienie 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 przypadku, 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 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 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. 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)
            }
        ),
        label = "size"
    )
}

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