Narzędzie Compose ma wiele wbudowanych mechanizmów animacji i wybór jednego z nich może być przytłaczający. Poniżej znajdziesz listę typowych zastosowań animacji. Więcej szczegółowych informacji na temat pełnego zestawu różnych dostępnych opcji interfejsu API znajdziesz w pełnej dokumentacji tworzenia animacji.
Animuj wspólne właściwości elementów kompozycyjnych
Compose zapewnia wygodne interfejsy API, które pozwalają rozwiązać wiele typowych przypadków użycia animacji. W tej sekcji pokazujemy, jak animować typowe właściwości funkcji kompozycyjnej.
Animacja pojawianie się / znikanie
Użyj AnimatedVisibility
, aby ukryć lub wyświetlić funkcję kompozycyjną. Dzieci w domenie AnimatedVisibility
mogą używać Modifier.animateEnterExit()
do tworzenia własnych przejść do wejścia lub wyjś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 AnimatedVisibility
pozwalają skonfigurować zachowanie funkcji kompozycyjnej, gdy jest wyświetlana i znika. Więcej informacji znajdziesz w pełnej dokumentacji.
Innym sposobem animowania widoczności funkcji kompozycyjnej jest animowanie wersji alfa w czasie za pomocą funkcji 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 pozostaje w kompozycji i nadal zajmuje przestrzeń, w której jest umieszczona. Mogło to spowodować, że czytniki ekranu i inne mechanizmy ułatwień dostępu nadal będą uwzględniać element na ekranie. Z drugiej strony AnimatedVisibility
usuwa w końcu element z kompozycji.
Animuj kolor tła
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()
.
W przypadku jednorazowego ustawienia kolorów możesz użyć atrybutu Modifier.background()
, ale jeśli animujesz kolor w miarę upływu czasu, może to spowodować więcej zmian kompozycji, niż jest to konieczne.
O nieskończonej animowaniu koloru tła znajdziesz w sekcji o powtarzaniu sekcji animacji.
Animuj rozmiar elementu kompozycyjnego
Funkcja tworzenia umożliwia animowanie rozmiaru elementów kompozycyjnych na kilka różnych sposobów. Używaj animateContentSize()
do tworzenia animacji między zmianami rozmiaru, które można tworzyć.
Jeśli na przykład masz pole zawierające tekst, który można rozwinąć do wielu wierszy, użyj funkcji Modifier.animateContentSize()
, by zapewnić płynniejsze 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ć właściwości AnimatedContent
z SizeTransform
, aby opisać, jak powinny być wprowadzane zmiany rozmiaru.
Animuj położenie funkcji kompozycyjnej
Aby animować położenie funkcji kompozycyjnej, użyj właściwości Modifier.offset{ }
w połączeniu z funkcją 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 } )
Jeśli chcesz, aby elementy kompozycyjne nie były rysowane na innych elementach kompozycyjnych ani pod nimi podczas animowania położenia lub rozmiaru, użyj właściwości Modifier.layout{ }
. Ten modyfikator propaguje zmiany rozmiaru i pozycji do elementu nadrzędnego, co z kolei wpływa na inne elementy podrzędne.
Jeśli np. przenosisz element Box
w obrębie elementu Column
, a inne elementy podrzędne muszą się przemieszczać razem z elementem Box
, uwzględnij informacje o przesunięciu za pomocą parametru Modifier.layout{ }
w ten 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) ) }
Animowane dopełnienie elementu kompozycyjnego
Aby animować dopełnienie funkcji kompozycyjnej, użyj właściwości animateDpAsState
w połączeniu z parametrem 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
Aby animować wysokość elementu kompozycyjnego, użyj właściwości animateDpAsState
w połączeniu z funkcją Modifier.graphicsLayer{ }
. W przypadku jednorazowej zmiany wysokości użyj parametru Modifier.shadow()
. Jeśli animujesz cień, bardziej skutecznym rozwiązaniem jest modyfikator Modifier.graphicsLayer{ }
.
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 względnej na różne wartości dla każdego stanu.
Animuj skalę, przesunięcie i obrót tekstu
Podczas animowania skali, przesunięcia lub obrotu tekstu ustaw parametr textMotion
na TextStyle
na TextMotion.Animated
. Zapewnia to płynniejsze przejścia między animacjami tekstowymi. Użyj narzędzia Modifier.graphicsLayer{ }
, aby przetłumaczyć, obrócić lub 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
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
Używaj funkcji AnimatedContent
, aby przełączać się między różnymi elementami kompozycyjnymi. Jeśli chcesz, aby elementy kompozycyjne były animowane, użyj funkcji 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 przejść, Więcej informacji znajdziesz w dokumentacji na stronie AnimatedContent
oraz w tym poście na blogu
AnimatedContent
.
Animuj podczas nawigacji do różnych miejsc
Aby animować przejścia między elementami kompozycyjnymi, gdy używany jest artefakt nawigacja-kompozycje, w funkcji kompozycyjnej określ enterTransition
i exitTransition
. Możesz też ustawić animację domyślną do używania we wszystkich miejscach 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( // ... ) } }
Jest wiele różnych rodzajów przejść wejścia i wyjścia, które mają odmienne efekty w stosunku do treści przychodzących i wychodzących. Więcej informacji znajdziesz w dokumentacji.
Powtarzanie animacji
Użyj rememberInfiniteTransition
z infiniteRepeatable
animationSpec
, by powtarzać animację w sposób ciągły. Zmień RepeatModes
, aby określić, w jaki sposób ma się poruszać wstecz i z powrotem.
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. Przy uruchamianiu funkcji kompozycyjnej uruchamia animację, którą można wykorzystać do zmiany stanu animacji. Użycie Animatable
z metodą animateTo
do uruchomienia animacji po uruchomieniu:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Twórz sekwencyjne animacje
Interfejsy API współprogramu Animatable
umożliwiają tworzenie sekwencyjnych lub równoczesnych animacji. Wywoływanie animateTo
w Animatable
jeden po drugim sprawia, że każda animacja czeka na zakończenie poprzednich animacji przed kontynuacją .
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
Aby wyświetlać równoczesne animacje, użyj interfejsów API współrzędnych (Animatable#animateTo()
lub animate
) albo interfejsu API Transition
. Jeśli używasz wielu funkcji uruchamiania w kontekście współpracy, uruchamiają one animacje w tym samym czasie:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Za pomocą interfejsu API updateTransition
możesz używać tego samego stanu do uruchamiania wielu różnych animacji właściwości jednocześnie. W poniższym przykładzie animowane są 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: szybkiego przesuwania lub zmiany pikseli na ekranie, klatka po klatce w celu utworzenia iluzji ruchu.
Weź pod uwagę różne fazy tworzenia wiadomości: kompozycję, układ i rysowanie. Jeśli animacja zmieni się w fazie układu, wszystkie elementy kompozycyjne, których dotyczy problem, muszą zostać przekazane i przerysowane. Jeśli animacja ma miejsce w fazie rysowania, domyślnie jest ona wydajniejsza niż w przypadku uruchamiania w fazie układu, bo miałoby to mniej nakładu pracy.
Aby mieć pewność, że aplikacja będzie robić jak najmniej animacji podczas tworzenia animacji, w miarę możliwości wybierz wersję lambda funkcji Modifier
. Spowoduje to pominięcie zmiany kompozycji i wykonanie animacji poza fazą kompozycji. W przeciwnym razie użyj metody Modifier.graphicsLayer{ }
, ponieważ modyfikator zawsze działa w fazie rysowania. Więcej informacji na ten temat znajdziesz w sekcji dotyczącej odwracania odczytów w dokumentacji wydajności.
Zmiana czasu animacji
Domyślnie w tworzeniu większości animacji używane są animacje wiosenne. Sprężyny, czyli animacje
oparte na fizyce, wyglądają bardziej naturalnie. Są również przerywane, ponieważ uwzględniają bieżącą prędkość obiektu, a nie stały czas.
Jeśli chcesz zastąpić ustawienia domyślne, we wszystkich pokazanych powyżej interfejsach API animacji możesz ustawić animationSpec
, aby dostosować sposób działania animacji, niezależnie od tego, czy ma ona być wykonywana przez określony czas, czy ma być bardziej płynna.
Oto podsumowanie różnych opcji animationSpec
:
spring
: animacja oparta na fizyce, domyślna dla wszystkich animacji. Możesz zmienić sztywność lub współczynnik tłumienia, aby uzyskać inny wygląd i styl animacji.tween
(skrót od Between): animacja zależna od czasu trwania, która wyświetla się między 2 wartościami przy użyciu funkcjiEasing
.keyframes
: specyfikacja określania wartości w konkretnych kluczowych momentach animacji.repeatable
: specyfikacja oparta na czasie trwania, która działa określoną liczbę razy, określona jakoRepeatMode
.infiniteRepeatable
: specyfikacja oparta na czasie trwania, która działa bezterminowo.snap
: automatycznie przyciąga reklamę do wartości końcowej bez żadnej animacji.
Więcej informacji na temat animationSpecs znajdziesz w pełnej dokumentacji.
Dodatkowe materiały
Więcej przykładów zabawnych animacji w funkcji tworzenia wiadomości znajdziesz na tych stronach:
- 5 szybkich animacji w funkcji Utwórz
- Poruszanie się wśród meduzy
- Dostosowywanie elementu
AnimatedContent
w funkcji Compose - Wygładzanie funkcji wygładzania w narzędziu Compose