Compose ma wiele wbudowanych mechanizmów animacji, więc wybór odpowiedniego może być trudny. Poniżej znajdziesz listę typowych zastosowań animacji. Szczegółowe informacje o pełnym zestawie dostępnych opcji interfejsu API znajdziesz w dokumentacji Compose Animation.
Animowanie typowych właściwości komponentów
Compose udostępnia wygodne interfejsy API, które pozwalają rozwiązać wiele typowych problemów związanych z animacjami. W tej sekcji pokazujemy, jak animować typowe właściwości komponentu kompozycyjnego.
Animowanie pojawiania się i znikania

Użyj AnimatedVisibility
, aby ukryć lub wyświetlić element kompozycyjny. Dzieci w wieku AnimatedVisibility
mogą używać Modifier.animateEnterExit()
do własnych animacji 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
umożliwiają skonfigurowanie sposobu działania elementu kompozycyjnego, gdy się pojawia i znika. Więcej informacji znajdziesz w pełnej dokumentacji.
Inną opcją animowania widoczności komponentu jest animowanie wartości 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) ) { }
Zmiana wartości alfa wiąże się jednak z tym, że komponent pozostaje w kompozycji i nadal zajmuje miejsce, w którym został umieszczony. Może to spowodować, że czytniki ekranu i inne mechanizmy ułatwień dostępu nadal będą traktować element jako widoczny na ekranie. Z drugiej strony AnimatedVisibility
ostatecznie usuwa
element z kompozycji.

Animowanie koloru 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 bardziej wydajna niż używanie Modifier.background()
.
Modifier.background()
jest dopuszczalne w przypadku jednorazowego ustawienia koloru, ale podczas animowania koloru w czasie może powodować więcej ponownych kompozycji niż jest to konieczne.
Informacje o nieskończonym animowaniu koloru tła znajdziesz w sekcji dotyczącej powtarzania animacji.
Animowanie rozmiaru komponentu

Compose umożliwia animowanie rozmiaru komponentów na kilka sposobów. Używaj
animateContentSize()
w przypadku animacji między zmianami rozmiaru komponentu.
Jeśli na przykład masz pole tekstowe, które może rozwinąć się z jednego wiersza do wielu, możesz użyć funkcji 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 mają przebiegać zmiany rozmiaru.
Animowanie pozycji komponentu

Aby animować pozycję komponentu, 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 } )
Jeśli chcesz mieć pewność, że elementy kompozycyjne nie będą rysowane nad ani pod innymi elementami kompozycyjnymi podczas animowania pozycji lub rozmiaru, użyj Modifier.layout{ }
. Ten modyfikator propaguje zmiany rozmiaru i pozycji do elementu nadrzędnego, co następnie wpływa na inne elementy podrzędne.
Jeśli na przykład przenosisz element Box
w ramach elementu Column
, a inne elementy podrzędne muszą się przesuwać wraz z elementem Box
, uwzględnij informacje o przesunięciu w elemencie 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) ) }

Modifier.layout{ }
Animowanie dopełnienia komponentu

Aby animować dopełnienie elementu kompozycyjnego, 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 } )
Animowanie wysokości elementu kompozycyjnego
Aby animować uniesienie komponentu, użyj atrybutu animateDpAsState
w połączeniu z atrybutem Modifier.graphicsLayer{ }
. W przypadku jednorazowych zmian wysokości użyj
Modifier.shadow()
. Jeśli animujesz cień, użycie modyfikatora Modifier.graphicsLayer{ }
jest bardziej wydajne.
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ć komponentu Card
i ustawić właściwość elevation na różne wartości w zależności od stanu.
Animowanie skali, przesunięcia lub obrotu tekstu

Podczas animowania skalowania, translacji lub obrotu tekstu ustaw parametr textMotion
na TextStyle
na TextMotion.Animated
. Zapewnia to płynniejsze przejścia między animacjami tekstu. Użyj 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) ) }
Animowanie koloru tekstu

Aby animować kolor tekstu, użyj lambdy color
w funkcji kompozycyjnej 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 między różnymi typami treści

Użyj AnimatedContent
, aby animować przejścia między różnymi komponentami, a jeśli chcesz tylko standardowego zanikania między komponentami, 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 przejść wejścia i wyjścia. Więcej informacji znajdziesz w dokumentacji na stronie AnimatedContent
lub w tym poście na blogu
AnimatedContent
.
Animowanie podczas nawigacji do różnych miejsc docelowych

Aby animować przejścia między funkcjami kompozycyjnymi podczas korzystania z artefaktu navigation-compose, określ właściwości enterTransition
i exitTransition
w funkcji kompozycyjnej. 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 -> ScreenDetails( // ... ) } }
Istnieje wiele różnych rodzajów przejść wejścia i wyjścia, które wywołują różne efekty na treściach przychodzących i wychodzących. Więcej informacji znajdziesz w dokumentacji.
Powtarzanie animacji

Użyj rememberInfiniteTransition
z infiniteRepeatable
animationSpec
, aby animacja była powtarzana w nieskończoność. Zmień RepeatModes
, aby określić, jak ma się poruszać w przód i w tył.
Użyj finiteRepeatable
, aby powtórzyć daną 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 }
Uruchamianie animacji po uruchomieniu funkcji kompozycyjnej
Funkcja LaunchedEffect
jest uruchamiana, gdy funkcja kompozycyjna wchodzi w skład kompozycji. Uruchamia animację po uruchomieniu komponentu, dzięki czemu możesz sterować zmianą stanu animacji. Używanie Animatable
z metodą animateTo
do uruchamiania animacji po uruchomieniu:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Tworzenie animacji sekwencyjnych

Użyj interfejsów API Animatable
coroutine, aby wykonywać animacje sekwencyjne lub współbieżne. Wywołanie animateTo
na Animatable
jeden po drugim powoduje, że każda animacja czeka na zakończenie poprzednich animacji, zanim przejdzie dalej .
Dzieje się tak, ponieważ jest to funkcja zawieszania.
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

Użyj interfejsów API do współprogramów (Animatable#animateTo()
lub animate
) albo interfejsu Transition
API, aby uzyskać równoczesne animacje. Jeśli w kontekście korutyny użyjesz kilku funkcji uruchamiania, animacje zostaną uruchomione w tym samym czasie:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Możesz użyć interfejsu updateTransition
API, aby używać tego samego stanu do sterowania wieloma różnymi animacjami właściwości w tym samym czasie. W przykładzie poniżej 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 } }
Optymalizowanie działania animacji
Animacje w Compose mogą powodować problemy z wydajnością. Wynika to z charakteru animacji: szybko zmieniających się pikseli na ekranie, klatka po klatce, aby stworzyć iluzję ruchu.
Pamiętaj o różnych fazach Compose: kompozycji, układzie i rysowaniu. Jeśli animacja zmienia fazę układu, wymaga ponownego ułożenia i narysowania wszystkich powiązanych komponentów. Jeśli animacja występuje w fazie rysowania, domyślnie będzie działać wydajniej niż w fazie układu, ponieważ będzie miała mniej pracy do wykonania.
Aby aplikacja wykonywała jak najmniej działań podczas animacji, w miarę możliwości wybieraj wersję lambda Modifier
. Pomija to ponowne komponowanie i wykonuje animację poza fazą komponowania. W przeciwnym razie użyj Modifier.graphicsLayer{ }
, ponieważ ten modyfikator zawsze działa w fazie rysowania. Więcej informacji znajdziesz w sekcji odraczanie odczytów w dokumentacji dotyczącej wydajności.
Zmienianie czasu animacji
Domyślnie Compose używa animacji sprężynowych w przypadku większości animacji. Animacje sprężynowe lub 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 interfejsy API animacji przedstawione powyżej umożliwiają ustawienie animationSpec
, aby dostosować sposób działania animacji, np. określić czas jej trwania lub sprawić, że będzie bardziej sprężysta.
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 działanie animacji.tween
(skrót od between): animacja oparta na czasie trwania, animuje przejście między dwiema wartościami za pomocą funkcjiEasing
.keyframes
: specyfikacja umożliwiająca określanie wartości w określonych kluczowych punktach animacji.repeatable
: specyfikacja oparta na czasie trwania, która jest uruchamiana określoną liczbę razy, podaną w parametrzeRepeatMode
.infiniteRepeatable
: Specyfikacja oparta na czasie trwania, która działa bezterminowo.snap
: natychmiastowe przejście 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 w tych artykułach:
- 5 szybkich animacji w Compose
- Animowanie meduzy w Compose
- Dostosowywanie
AnimatedContent
w trybie tworzenia - Wprowadzenie do funkcji Easing w Compose