Krótki przewodnik po animacjach w sekcji Tworzenie

Funkcja tworzenia wiadomości ma wiele wbudowanych mechanizmów animacji i może być przytłaczająca. musisz wybrać. Poniżej znajdziesz listę typowych zastosowań animacji. Dla: bardziej szczegółowe informacje na temat pełnego zestawu różnych dostępnych opcji interfejsu API. przeczytaj pełną dokumentację tworzenia animacji.

Animuj wspólne właściwości elementów kompozycyjnych

Compose zapewnia wygodne interfejsy API, które pozwalają rozwiązać wiele typowych problemów w przypadku animacji. W tej sekcji pokazujemy, jak animować typowe właściwości elementu kompozycyjnego.

Animacja pojawianie się / znikanie

Zielony element kompozycyjny, który wyświetla się i ukrywa
Rysunek 1. Animacja wyglądu i zniknięcia elementu w kolumnie
.

Użyj AnimatedVisibility, aby ukryć lub wyświetlić funkcję kompozycyjną. Dzieci w środku AnimatedVisibility może użyć karty Modifier.animateEnterExit() do własnego zgłoszenia lub wyjść z przejścia.

var visible by remember {
    mutableStateOf(true)
}
// Animated visibility will eventually remove the item from the composition once the animation has finished.
AnimatedVisibility(visible) {
    // your composable here
    // ...
}

Parametry wejścia i wyjścia parametru AnimatedVisibility umożliwiają skonfigurowanie funkcja kompozycyjna zachowuje się, gdy pojawia się i znika. Przeczytaj w całości dokumentacji.

Inną opcją animowania widoczności funkcji kompozycyjnej jest alfa na przestrzeni czasu za pomocą animateFloatAsState:

var visible by remember {
    mutableStateOf(true)
}
val animatedAlpha by animateFloatAsState(
    targetValue = if (visible) 1.0f else 0f,
    label = "alpha"
)
Box(
    modifier = Modifier
        .size(200.dp)
        .graphicsLayer {
            alpha = animatedAlpha
        }
        .clip(RoundedCornerShape(8.dp))
        .background(colorGreen)
        .align(Alignment.TopCenter)
) {
}

Jednak zmiana alfa wiąże się z zastrzeżeniem, że funkcja kompozycyjna pozostanie niezmieniona w kompozycji i nadal zajmuje przestrzeń, Ten może sprawić, że czytniki ekranu i inne mechanizmy ułatwień dostępu elementu na ekranie. Z drugiej strony AnimatedVisibility w końcu usuwa obiektu z kompozycji.

Animowanie wersji alfa funkcji kompozycyjnej
Rysunek 2. Animowanie wersji alfa funkcji kompozycyjnej
.

Animuj kolor tła

Funkcja kompozycyjna, której kolor tła zmienia się z czasem jako animacja, w której kolory przechodzą na siebie.
Rysunek 3. Animowanie koloru tła w trybie kompozycyjnym
.

val animatedColor by animateColorAsState(
    if (animateBackgroundColor) colorGreen else colorBlue,
    label = "color"
)
Column(
    modifier = Modifier.drawBehind {
        drawRect(animatedColor)
    }
) {
    // your composable here
}

Ta opcja jest skuteczniejsza niż Modifier.background(). Dla jednego ustawienia koloru można zastosować Modifier.background(), ale gdy animowanie koloru w czasie, może spowodować większą liczbę zmian kompozycji niż niezbędną.

Nieskończoną animację koloru tła znajdziesz w sekcji powtarzanie animacji .

Animuj rozmiar elementu kompozycyjnego

Rozmiar zielonego elementu kompozycyjnego zmienia się płynnie.
Rysunek 4. kompozycyjna płynna animacja między małym a większym rozmiarem
.

Funkcja tworzenia umożliwia animowanie rozmiaru elementów kompozycyjnych na kilka różnych sposobów. Używaj animateContentSize() – animacje między zmianami rozmiaru kompozycyjnego.

Jeśli na przykład masz pole zawierające tekst, które można rozwinąć od 1 do Możesz użyć wielu linii Modifier.animateContentSize(), aby zwiększyć płynność przejście:

var expanded by remember { mutableStateOf(false) }
Box(
    modifier = Modifier
        .background(colorBlue)
        .animateContentSize()
        .height(if (expanded) 400.dp else 200.dp)
        .fillMaxWidth()
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            expanded = !expanded
        }

) {
}

Możesz też użyć atrybutu AnimatedContent z SizeTransform, aby opisać jak powinny wprowadzać się zmiany rozmiaru.

Animuj położenie funkcji kompozycyjnej

Funkcja kompozycyjna w kolorze zielonym płynnie animowana w dół i w prawo
Rysunek 5. Funkcja kompozycyjna przesuwająca się o przesunięcie
.

Aby animować położenie funkcji kompozycyjnej, użyj operatora Modifier.offset{ } w połączeniu z animateIntOffsetAsState()

var moved by remember { mutableStateOf(false) }
val pxToMove = with(LocalDensity.current) {
    100.dp.toPx().roundToInt()
}
val offset by animateIntOffsetAsState(
    targetValue = if (moved) {
        IntOffset(pxToMove, pxToMove)
    } else {
        IntOffset.Zero
    },
    label = "offset"
)

Box(
    modifier = Modifier
        .offset {
            offset
        }
        .background(colorBlue)
        .size(100.dp)
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            moved = !moved
        }
)

Aby elementy kompozycyjne nie były rysowane nad ani pod innymi elementów kompozycyjnych podczas animowania pozycji lub rozmiaru, użyj funkcji Modifier.layout{ }. Ten powoduje przekazywanie zmian rozmiaru i pozycji do elementu nadrzędnego, co wpływa na innych dzieci.

Na przykład jeśli przenosisz element Box w elemencie Column i innych elementach podrzędnych które musisz przesunąć, gdy przesuwa się Box, uwzględnij informacje o przesunięciu Modifier.layout{ } w następujący sposób:

var toggled by remember {
    mutableStateOf(false)
}
val interactionSource = remember {
    MutableInteractionSource()
}
Column(
    modifier = Modifier
        .padding(16.dp)
        .fillMaxSize()
        .clickable(indication = null, interactionSource = interactionSource) {
            toggled = !toggled
        }
) {
    val offsetTarget = if (toggled) {
        IntOffset(150, 150)
    } else {
        IntOffset.Zero
    }
    val offset = animateIntOffsetAsState(
        targetValue = offsetTarget, label = "offset"
    )
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(colorBlue)
    )
    Box(
        modifier = Modifier
            .layout { measurable, constraints ->
                val offsetValue = if (isLookingAhead) offsetTarget else offset.value
                val placeable = measurable.measure(constraints)
                layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) {
                    placeable.placeRelative(offsetValue)
                }
            }
            .size(100.dp)
            .background(colorGreen)
    )
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(colorBlue)
    )
}

2 pola, w których druga ramka animuje swoje położenie X i Y, a trzecia odpowiada, poruszając się również o Y.
Rysunek 6. Animowanie w aplikacji Modifier.layout{ }

Animowane dopełnienie elementu kompozycyjnego

Zielona funkcja kompozycyjna staje się coraz mniejsza z każdym kliknięciem, z animowanym dopełnieniem
Rysunek 7. Kompozycyjne z animowanym dopełnieniem
.

Aby animować dopełnienie funkcji kompozycyjnej, użyj właściwości animateDpAsState w połączeniu z Modifier.padding():

var toggled by remember {
    mutableStateOf(false)
}
val animatedPadding by animateDpAsState(
    if (toggled) {
        0.dp
    } else {
        20.dp
    },
    label = "padding"
)
Box(
    modifier = Modifier
        .aspectRatio(1f)
        .fillMaxSize()
        .padding(animatedPadding)
        .background(Color(0xff53D9A1))
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            toggled = !toggled
        }
)

Animuj wysokość elementu kompozycyjnego

Rys. 8. Animacja wysokości elementu kompozycyjnego po kliknięciu

Aby animować wysokość elementu kompozycyjnego, użyj funkcji animateDpAsState w połączeniu z Modifier.graphicsLayer{ } W przypadku jednorazowej zmiany wysokości użyj funkcji Modifier.shadow() Jeśli animujesz cień, za pomocą funkcji Modyfikator Modifier.graphicsLayer{ } jest skuteczniejszą opcją.

val mutableInteractionSource = remember {
    MutableInteractionSource()
}
val pressed = mutableInteractionSource.collectIsPressedAsState()
val elevation = animateDpAsState(
    targetValue = if (pressed.value) {
        32.dp
    } else {
        8.dp
    },
    label = "elevation"
)
Box(
    modifier = Modifier
        .size(100.dp)
        .align(Alignment.Center)
        .graphicsLayer {
            this.shadowElevation = elevation.value.toPx()
        }
        .clickable(interactionSource = mutableInteractionSource, indication = null) {
        }
        .background(colorGreen)
) {
}

Możesz też użyć funkcji kompozycyjnej Card i ustawić właściwość wysokości nad poziomem morza na różne wartości dla każdego stanu.

Animuj skalę, przesunięcie i obrót tekstu

Tekst do kompozycji mówi
Rysunek 9. Płynne animowanie tekstu między dwoma rozmiarami
.

Podczas animowania skali, przesunięcia lub obrotu tekstu ustaw textMotion od TextStyle do TextMotion.Animated. Zapewnia to płynniejsze działanie przejścia między animacjami tekstowymi. Użyj aplikacji Modifier.graphicsLayer{ }, aby: przetłumaczyć, obrócić ani przeskalować tekst.

val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
val scale by infiniteTransition.animateFloat(
    initialValue = 1f,
    targetValue = 8f,
    animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
    label = "scale"
)
Box(modifier = Modifier.fillMaxSize()) {
    Text(
        text = "Hello",
        modifier = Modifier
            .graphicsLayer {
                scaleX = scale
                scaleY = scale
                transformOrigin = TransformOrigin.Center
            }
            .align(Alignment.Center),
        // Text composable does not take TextMotion as a parameter.
        // Provide it via style argument but make sure that we are copying from current theme
        style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated)
    )
}

Animuj kolor tekstu

Słowa
Rysunek 10. Przykład animowanego koloru tekstu
.

Aby animować kolor tekstu, użyj funkcji lambda color w komponencie kompozycyjnym BasicText:

val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
val animatedColor by infiniteTransition.animateColor(
    initialValue = Color(0xFF60DDAD),
    targetValue = Color(0xFF4285F4),
    animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
    label = "color"
)

BasicText(
    text = "Hello Compose",
    color = {
        animatedColor
    },
    // ...
)

Przełączanie się między różnymi typami treści

Zielony ekran mówi
Rysunek 11. Używanie AnimatedContent do animowania zmian między różnymi elementami kompozycyjnymi (spowolnienie)
.

Użyj AnimatedContent, aby przełączać się między różnymi elementami kompozycyjnymi, jeśli: aby standardowe rozjaśnianie elementów kompozycyjnych, użyj Crossfade.

var state by remember {
    mutableStateOf(UiState.Loading)
}
AnimatedContent(
    state,
    transitionSpec = {
        fadeIn(
            animationSpec = tween(3000)
        ) togetherWith fadeOut(animationSpec = tween(3000))
    },
    modifier = Modifier.clickable(
        interactionSource = remember { MutableInteractionSource() },
        indication = null
    ) {
        state = when (state) {
            UiState.Loading -> UiState.Loaded
            UiState.Loaded -> UiState.Error
            UiState.Error -> UiState.Loading
        }
    },
    label = "Animated Content"
) { targetState ->
    when (targetState) {
        UiState.Loading -> {
            LoadingScreen()
        }
        UiState.Loaded -> {
            LoadedScreen()
        }
        UiState.Error -> {
            ErrorScreen()
        }
    }
}

AnimatedContent można dostosować, tak aby wyświetlać wiele różnych rodzajów wiadomości przejściach typu wyjściowego. Więcej informacji znajdziesz w dokumentacji AnimatedContent lub przeczytaj tego posta na blogu AnimatedContent

Animuj podczas nawigacji do różnych miejsc

2 kompozycje, jeden zielony z napisem „Lądowanie”, a drugi niebieski z nazwą „Szczegóły” animowany przez przesuwanie kompozycyjnego detalu po stronie docelowej.
Rysunek 12. Przełączanie się między elementami kompozycyjnymi przy użyciu funkcji nawigacji i tworzenia.
.

Aby animować przejścia między elementami kompozycyjnymi, gdy używany jest artefakt nawigowanie-compose, określ parametry enterTransition oraz exitTransition w funkcji kompozycyjnej. Możesz też ustawić animację domyślną używany w przypadku wszystkich miejsc docelowych na najwyższym poziomie (NavHost):

val navController = rememberNavController()
NavHost(
    navController = navController, startDestination = "landing",
    enterTransition = { EnterTransition.None },
    exitTransition = { ExitTransition.None }
) {
    composable("landing") {
        ScreenLanding(
            // ...
        )
    }
    composable(
        "detail/{photoUrl}",
        arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }),
        enterTransition = {
            fadeIn(
                animationSpec = tween(
                    300, easing = LinearEasing
                )
            ) + slideIntoContainer(
                animationSpec = tween(300, easing = EaseIn),
                towards = AnimatedContentTransitionScope.SlideDirection.Start
            )
        },
        exitTransition = {
            fadeOut(
                animationSpec = tween(
                    300, easing = LinearEasing
                )
            ) + slideOutOfContainer(
                animationSpec = tween(300, easing = EaseOut),
                towards = AnimatedContentTransitionScope.SlideDirection.End
            )
        }
    ) { backStackEntry ->
        ScreenDetails(
            // ...
        )
    }
}

Istnieje wiele różnych rodzajów przejść, w zależności od charakteru treści przychodzących i wychodzących, zapoznaj się z dokumentacja.

Powtarzanie animacji

Zielone tło, które w nieskończoność przekształca się w niebieskie tło, które wyświetla się w wybranym kolorze.
Rysunek 13. Nieskończone animowanie koloru tła między 2 wartościami
.

Użyj domeny rememberInfiniteTransition w połączeniu z: infiniteRepeatable animationSpec, aby powtarzać animację w sposób ciągły. Zmień RepeatModes na i określić, jak ma się to odbywać.

Użyj funkcji finiteRepeatable, aby powtórzyć określoną liczbę razy.

val infiniteTransition = rememberInfiniteTransition(label = "infinite")
val color by infiniteTransition.animateColor(
    initialValue = Color.Green,
    targetValue = Color.Blue,
    animationSpec = infiniteRepeatable(
        animation = tween(1000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    ),
    label = "color"
)
Column(
    modifier = Modifier.drawBehind {
        drawRect(color)
    }
) {
    // your composable here
}

Włącz animację przy uruchomieniu funkcji kompozycyjnej

Narzędzie LaunchedEffect jest uruchamiane, gdy do kompozycji pojawi się funkcja kompozycyjna. Zaczyna się możesz utworzyć animację przy uruchomieniu funkcji kompozycyjnej, zmiany stanu. Użycie Animatable z metodą animateTo do uruchamiania animacja przy uruchomieniu:

val alphaAnimation = remember {
    Animatable(0f)
}
LaunchedEffect(Unit) {
    alphaAnimation.animateTo(1f)
}
Box(
    modifier = Modifier.graphicsLayer {
        alpha = alphaAnimation.value
    }
)

Twórz sekwencyjne animacje

Cztery okręgi z animowanymi zielonymi strzałkami między nimi, animowanymi po kolei.
Rysunek 14. Schemat pokazujący po kolei, jak rozwija się animacja sekwencyjna.

Używaj wewnętrznych interfejsów API Animatable do wykonywania sekwencyjnych lub równoczesnych ani animacji. Wywołuję animateTo (Animatable) jeden po drugim Kolejne animacje będą czekać na zakończenie poprzednich animacji . Dzieje się tak, ponieważ jest to funkcja zawieszenia.

val alphaAnimation = remember { Animatable(0f) }
val yAnimation = remember { Animatable(0f) }

LaunchedEffect("animationKey") {
    alphaAnimation.animateTo(1f)
    yAnimation.animateTo(100f)
    yAnimation.animateTo(500f, animationSpec = tween(100))
}

Tworzenie równoczesnych animacji

Trzy animowane okręgi z zielonymi strzałkami, które są połączone w tym samym czasie.
Rysunek 15. Diagram pokazujący postęp równoczesnych animacji.

użyj wewnętrznych interfejsów API (Animatable#animateTo() lub animate); interfejs API Transition do tworzenia równoczesnych animacji. Jeśli używasz wielu funkcji gdy funkcje są uruchamiane w kontekście, uruchamiają animacje w tym samym godzina:

val alphaAnimation = remember { Animatable(0f) }
val yAnimation = remember { Animatable(0f) }

LaunchedEffect("animationKey") {
    launch {
        alphaAnimation.animateTo(1f)
    }
    launch {
        yAnimation.animateTo(100f)
    }
}

Można użyć interfejsu API updateTransition, aby użyć tego samego stanu do kierowania możesz korzystać z wielu różnych animacji. Animowany przykład poniżej 2 właściwości kontrolowane przez zmianę stanu: rect i borderWidth:

var currentState by remember { mutableStateOf(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "transition")

val rect by transition.animateRect(label = "rect") { state ->
    when (state) {
        BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
        BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
    }
}
val borderWidth by transition.animateDp(label = "borderWidth") { state ->
    when (state) {
        BoxState.Collapsed -> 1.dp
        BoxState.Expanded -> 0.dp
    }
}

Optymalizacja wydajności animacji

Animacje w Compose mogą powodować problemy z wydajnością. Wynika to z natury animacji: szybkie przesuwanie lub zmienianie pikseli na ekranie. klatka po klatce, aby stworzyć iluzję ruchu.

Weź pod uwagę różne fazy tworzenia wiadomości: kompozycję, układ i rysowanie. Jeśli animacja zmienia fazę układu, przez co wszystkie elementy kompozycyjne, których dotyczy problem, muszą i przekazywać nowe informacje. Jeśli animacja ma miejsce w fazie rysowania, być domyślnie wydajniejsze niż w przypadku uruchamiania animacji w układzie ponieważ wykonanoby mniej pracy.

Aby mieć pewność, że podczas tworzenia animacji aplikacja będzie robić jak najwięcej, wybierz lambda wersji Modifier tam, gdzie to możliwe. Pomija to zmianę kompozycji i działa animacji poza fazą kompozycji, w przeciwnym razie użyj funkcji Modifier.graphicsLayer{ }, ponieważ ten modyfikator zawsze działa podczas rysowania te etapy. Więcej informacji na ten temat znajdziesz w sekcji o odwracaniu odczytów w artykule zapoznaj się z dokumentacją wyników.

Zmiana czasu animacji

Domyślnie w tworzeniu większości animacji używane są animacje wiosenne. Springs, oparte na fizyce, są bardziej naturalne. Są też przerywane, biorą one pod uwagę aktualną prędkość obiektu, a nie stały czas. Jeśli chcesz zastąpić ustawienia domyślne, użyj wszystkich interfejsów API animacji pokazanych powyżej możesz ustawić animationSpec, aby dostosować sposób działania animacji, czy wolisz, aby wykonywały się przez określony czas, czy były bardziej dynamiczne.

Oto podsumowanie różnych opcji animationSpec:

  • spring: animacja oparta na fizyce, domyślna dla wszystkich animacji. Ty można zmienić sztywność lub współczynnik tłumienia, aby uzyskać inną animację wygląd i styl.
  • tween (skrót od Between): animacja zależna od czasu trwania, animacja między 2 wartościami za pomocą funkcji Easing.
  • keyframes: specyfikacja określania wartości w konkretnych kluczowych punktach animację.
  • repeatable: specyfikacja oparta na czasie trwania, która działa określoną liczbę razy. określone przez RepeatMode.
  • infiniteRepeatable: specyfikacja oparta na czasie trwania, która działa bezterminowo.
  • snap: automatycznie przyciąga reklamę do wartości końcowej bez żadnej animacji.
.
Wpisz tutaj swój tekst alternatywny
Rysunek 16. Brak ustawionej specyfikacji w porównaniu ze specyfikacją niestandardowych sprężyn
.

Przeczytaj pełną dokumentację, aby dowiedzieć się więcej na temat parametru animationSpecs.

Dodatkowe materiały

Więcej przykładów zabawnych animacji w funkcji tworzenia wiadomości znajdziesz na tych stronach: