Compose ma wiele wbudowanych mechanizmów animacji i może być trudno zdecydować, który z nich wybrać. Poniżej znajdziesz listę typowych zastosowań animacji. Więcej informacji o pełnym zestawie różnych dostępnych opcji interfejsu API znajdziesz w dokumentacji animacji w Compose.
Animowanie typowych właściwości funkcji kompozycyjnych
Compose udostępnia wygodne interfejsy API, które pozwalają rozwiązać wiele typowych problemów związanych z animacjami. W tej sekcji dowiesz się, jak animować typowe właściwości funkcji kompozycyjnej.
Animuj pojawianie się i znikanie
Użyj AnimatedVisibility, aby ukryć lub wyświetlić element kompozycyjny. Elementy podrzędne wewnątrz AnimatedVisibility mogą używać Modifier.animateEnterExit() do własnego przejścia 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 elementu kompozycyjnego, gdy się pojawia i znika. Więcej informacji znajdziesz w pełnej dokumentacji.
Inną opcją animowania widoczności funkcji kompozycyjnej jest animowanie kanału alfa w czasie 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.TopCenckGuide.kt
Zmiana wartości alfa wiąże się jednak z tym, że element kompozycyjny pozostaje w kompozycji i nadal zajmuje miejsce, w którym jest rozmieszczony. Może to spowodować, że czytniki ekranu i inne mechanizmy ułatwień dostępu będą nadal uważać, że element jest na ekranie. Z kolei AnimatedVisibility ostatecznie usuwa 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 composackGuide.kt
Ta opcja jest bardziej wydajna niż użycie Modifier.background().
Modifier.background() jest akceptowalny w przypadku jednorazowego ustawienia koloru, ale podczas animowania koloru w czasie może to spowodować więcej rekompozycji niż jest to konieczne.
Informacje o nieskończonej animacji koloru tła znajdziesz w sekcji dotyczącej powtarzania animacji.
Animowanie rozmiaru elementu kompozycyjnego
Compose umożliwia animowanie rozmiaru komponentów na kilka różnych sposobów. Używaj animateContentSize() do animacji między zmianami rozmiaru funkcji kompozycyjnej.
Jeśli na przykład masz pole zawierające tekst, który może się rozszerzać z jednego do kilku wierszy, możesz użyć Modifier.animateContentSize(), aby uzyskać 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ć AnimatedContent z SizeTransform, aby opisać, jak powinny się zmieniać rozmiary.
Animowanie pozycji elementu kompozycyjnego
Aby animować pozycję funkcji kompozycyjnej, użyj 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 ckGuide.kt
Jeśli chcesz mieć pewność, że elementy kompozycyjne nie będą rysowane nad innymi elementami kompozycyjnymi ani pod nimi podczas animowania pozycji lub rozmiaru, użyj Modifier.layout{ }. Ten modyfikator przekazuje zmiany rozmiaru i pozycji do elementu nadrzędnego, co wpływa na inne elementy podrzędne.
Jeśli na przykład przenosisz element Box w elemencie Column, a inne elementy podrzędne muszą zostać przeniesione, gdy przenoszony jest element Box, dołącz informacje o przesunięciu za pomocą elementu 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, con>straints - 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(colorBluckGuide.kt
Modifier.layout{ }Animowanie dopełnienia elementu kompozycyjnego
Aby animować dopełnienie funkcji kompozycyjnej, użyj 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 ckGuide.kt
Animowanie wysokości elementu kompozycyjnego
Aby animować podniesienie elementu kompozycyjnego, użyj animateDpAsState w połączeniu z Modifier.graphicsLayer{ }. W przypadku jednorazowych zmian wysokości użyj Modifier.shadow(). Jeśli animujesz cień, bardziej wydajną opcją jest użycie modyfikatora 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(colorGrckGuide.kt
Możesz też użyć funkcji kompozycyjnej Card i ustawić właściwość wysokości na różne wartości w zależności od stanu.
Animowanie skali, przesunięcia lub obrotu tekstu
Podczas animowania skali, przesunięcia lub obrotu tekstu ustaw parametr textMotion w TextStyle na TextMotion.Animated. Dzięki temu przejścia między animacjami
tekstu będą płynniejsze. Użyj Modifier.graphicsLayer{ }, aby przesunąć, 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 = ) } AnimationQuickGuide.kt
Animowanie koloru tekstu
Aby animować kolor tekstu, użyj funkcji lambda color w komponencie 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 = { ani// ... )AnimationQuickGuide.kt
Przełączanie się między różnymi typami treści
Użyj AnimatedContent, aby animować przejścia między różnymi elementami kompozycyjnymi. Jeśli chcesz tylko standardowego zanikania między elementami kompozycyjnymi, 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" ) { tar>getState - when (targetState) { UiState>.Loading - { LoadingScreen() } UiStat>e.Loaded - { LoadedScreen() } UiSta>te.Error - { ErrorScreen() ckGuide.kt
AnimatedContent można dostosować, aby wyświetlać wiele różnych rodzajów przejść wejścia i wyjścia. Więcej informacji znajdziesz w dokumentacji dotyczącej AnimatedContent lub w tym poście na blogu na temat
AnimatedContent.
Animacja podczas przechodzenia do różnych miejsc docelowych
Aby animować przejścia między elementami kompozycyjnymi podczas korzystania z artefaktu navigation-compose, określ enterTransition i exitTransition w elemencie kompozycyjnym. Możesz też ustawić domyślną animację, która będzie używana 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 - ScreenDetai ) } }AnimationQuickGuide.kt
Istnieje wiele różnych rodzajów przejść wejścia i wyjścia, które stosują różne efekty do treści przychodzących i wychodzących. Więcej informacji znajdziesz w dokumentacji.
Powtarzanie animacji
Użyj rememberInfiniteTransition z infiniteRepeatable
animationSpec, aby animacja powtarzała się w nieskończoność. Zmień RepeatMode, aby określić, jak ma się poruszać.
Użyj repeatable, 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) } ) { // yoimationQuickGuide.kt
Uruchamianie animacji po uruchomieniu funkcji kompozycyjnej
LaunchedEffect jest uruchamiany, gdy element kompozycyjny wchodzi do kompozycji. Uruchamia animację po uruchomieniu funkcji kompozycyjnej. Możesz użyć tego do zmiany stanu animacji. Użycie Animatable z metodą animateTo, aby uruchomić animację przy uruchamianiu:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Tworzenie animacji sekwencyjnych
Używaj interfejsów API współprogramów Animatable do wykonywania animacji sekwencyjnych lub równoczesnych. Wywołanie metody animateTo na obiekcie Animatable powoduje, że każda animacja czeka na zakończenie poprzednich, zanim się rozpocznie.
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 = tweckGuide.kt
Tworzenie jednoczesnych animacji
Aby uzyskać jednoczesne animacje, użyj interfejsów API współprogramów (Animatable#animateTo() lub animate) albo interfejsu API Transition. Jeśli użyjesz wielu funkcji uruchamiania w kontekście współprogramu, animacje zostaną uruchomione jednocześnie:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100ckGuide.kt
Możesz użyć interfejsu API updateTransition, aby używać tego samego stanu do sterowania wieloma różnymi animacjami właściwości jednocześnie. W tym 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 = "r>ect") { state - when (state) { >BoxState.Collapsed - Rect(0f, 0f, 100f, 100f) > BoxState.Expanded - Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = &quo>t;borderWidth") { state - when (state) >{ BoxState.Collapsed - 1.>dp Bo } }AnimationQuickGuide.kt
Optymalizowanie wydajności animacji
Animacje w Compose mogą powodować problemy z wydajnością. Wynika to z charakteru animacji, która polega na szybkim przesuwaniu lub zmienianiu pikseli na ekranie klatka po klatce, aby stworzyć iluzję ruchu.
Weź pod uwagę różne fazy Compose: kompozycję, układ i rysowanie. Jeśli animacja zmienia fazę układu, wymaga ponownego ułożenia i przerysowania wszystkich elementów kompozycyjnych, których dotyczy. Jeśli animacja występuje w fazie rysowania, jest domyślnie bardziej wydajna niż w fazie układu, ponieważ wymaga mniej pracy.
Aby aplikacja wykonywała jak najmniej operacji podczas animacji, w miarę możliwości wybierz wersję lambda elementu Modifier. Dzięki temu pomijana jest rekompozycja, a animacja jest wykonywana poza fazą kompozycji. W przeciwnym razie użyj Modifier.graphicsLayer{ }, ponieważ ten modyfikator zawsze działa w fazie rysowania. Więcej informacji na ten temat znajdziesz w sekcji dotyczącej odraczania odczytów w dokumentacji na temat wydajności.
Zmienianie czasu animacji
W Compose większość animacji jest domyślnie sprężynowa. Sprężyny, czyli animacje oparte na fizyce, są bardziej naturalne. Można je też przerwać, ponieważ uwzględniają bieżącą prędkość obiektu, a nie stały czas.
Jeśli chcesz zastąpić ustawienia domyślne, wszystkie pokazane wcześniej interfejsy Animation API umożliwiają ustawienie animationSpec, aby dostosować sposób działania animacji, np. określić, czy ma być wykonywana przez określony czas, czy ma być bardziej dynamiczna.
Poniżej znajdziesz 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 oparta na czasie trwania, animuje między 2 wartościami za pomocą funkcjiEasing.keyframes: specyfikacja do określania wartości w określonych kluczowych punktach animacji.repeatable: specyfikacja oparta na czasie trwania, która jest uruchamiana określoną liczbę razy, określoną przezRepeatMode.infiniteRepeatable: specyfikacja oparta na czasie trwania, która działa bez końca.snap: natychmiast przechodzi do wartości końcowej bez animacji.
Więcej informacji o animationSpecs znajdziesz w pełnej dokumentacji.
Dodatkowe materiały
Więcej przykładów ciekawych animacji w Compose znajdziesz tutaj:
- 5 szybkich animacji w Compose
- Poruszanie meduzą w Compose
- Dostosowywanie elementu
AnimatedContentw Compose - Funkcje wygładzania w Compose