Funkcja tworzenia filmów ma wiele wbudowanych mechanizmów animacji i trudno jest zastanawiać się, który z nich wybrać. Poniżej znajduje się lista typowych przypadków użycia animacji. Szczegółowe informacje o pełnym zestawie różnych dostępnych opcji interfejsu API znajdziesz w pełnej dokumentacji funkcji tworzenia animacji.
Animacja wspólnych właściwości elementów kompozycyjnych
Interfejs Compose udostępnia wygodne interfejsy API, które pozwalają rozwiązywać typowe zastosowania animacji. W tej sekcji pokażemy, jak animować typowe właściwości funkcji kompozycyjnej.
Animacja pojawiania się / znikania
Użyj AnimatedVisibility
, aby ukryć lub wyświetlić kompozycję. Dzieci w domenie AnimatedVisibility
mogą używać Modifier.animateEnterExit()
jako własnego 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 właściwości AnimatedVisibility
pozwalają skonfigurować zachowanie elementu kompozycyjnego, gdy się pojawia i znika. Aby dowiedzieć się więcej, przeczytaj pełną dokumentację.
Inną opcją animowania widoczności funkcji kompozycyjnej jest animowanie wersji alfa w czasie za pomocą narzędzia 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 wersji alfa wiąże się jednak z zastrzeżeniem, że element kompozycyjny pozostały w kompozycji i nadal zajmuje miejsce, w którym jest ułożony. Może to spowodować, że czytniki ekranu i inne mechanizmy ułatwień dostępu nadal będą uwzględniać element widoczny na ekranie. Z drugiej strony AnimatedVisibility
usuwa element z kompozycji.
Animacja 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 skuteczniejsza niż użycie Modifier.background()
.
Modifier.background()
jest akceptowalny w przypadku ustawiania kolorów w ramach jednego zdjęcia, ale animowanie koloru w dłuższym okresie może spowodować powstanie większej liczby zmian kompozycji niż to konieczne.
Jeśli chcesz animować kolor tła w nieskończoność, przeczytaj sekcję o powtarzaniu sekcji animacji.
Animacja rozmiaru funkcji kompozycyjnej
Tworzenie umożliwia animowanie rozmiaru elementu kompozycyjnego na kilka różnych sposobów. Użyj animateContentSize()
do animacji między zmianami rozmiaru kompozycyjnego.
Jeśli na przykład masz pole zawierające tekst, który można rozwinąć z jednego do kilku wierszy, możesz użyć elementu 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ć sposób wprowadzania zmiany rozmiaru.
Animacja pozycji elementu kompozycyjnego
Aby animować pozycję funkcji kompozycyjnej, użyj funkcji 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 } )
Aby mieć pewność, że podczas animowania położenia lub rozmiaru kompozycje nie będą przeciągane nad lub pod innymi elementami kompozycyjnymi, użyj funkcji Modifier.layout{ }
. Ten modyfikator rozpowszechnia zmiany rozmiaru i pozycji w elemencie nadrzędnym, co wpływa na inne elementy podrzędne.
Jeśli na przykład przenosisz element Box
w obrębie elementu Column
, a inne elementy podrzędne muszą się przesunąć, gdy Box
się porusza, podaj informacje o przesunięciu za pomocą właściwości 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) ) }
Animacja dopełnienia funkcji kompozycyjnej
Aby animować dopełnienie funkcji kompozycyjnej, użyj funkcji animateDpAsState
w połączeniu z elementem 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 } )
Animacja wysokości elementu kompozycyjnego
Aby animować wysokość elementu kompozycyjnego, użyj funkcji animateDpAsState
w połączeniu z funkcją Modifier.graphicsLayer{ }
. Aby jednorazowo zmienić wysokość, użyj funkcji Modifier.shadow()
. Jeśli animujesz cień, skuteczniejszą 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(colorGreen) ) { }
Możesz też użyć funkcji kompozycyjnej Card
i ustawić właściwość height na różne wartości dla poszczególnych stanów.
Animacja skali, tłumaczenia lub obrotu tekstu
Podczas animowania skali, przesunięcia lub obrotu tekstu ustaw parametr textMotion
w elemencie TextStyle
na TextMotion.Animated
. Zapewnia to płynne przejścia między animacjami tekstowymi. Użyj narzędzia Modifier.graphicsLayer{ }
, aby przetłumaczyć, obrócić lub skalować 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 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 = { animatedColor }, // ... )
Przełączanie się między różnymi typami treści
AnimatedContent
służy do animowania różnych elementów kompozycyjnych. Jeśli chcesz po prostu użyć 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" ) { targetState -> when (targetState) { UiState.Loading -> { LoadingScreen() } UiState.Loaded -> { LoadedScreen() } UiState.Error -> { ErrorScreen() } } }
AnimatedContent
można dostosować, aby pokazywał wiele różnych rodzajów wejścia i wyjścia. Więcej informacji znajdziesz w dokumentacji na stronie AnimatedContent
lub w tym poście na blogu
AnimatedContent
.
Animuj podczas podróży
Aby animować przejścia między elementami kompozycyjnymi przy użyciu artefaktu navigation-compose, określ w nim enterTransition
i exitTransition
. 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 mają różne efekty w przypadku treści przychodzących i wychodzących. Więcej informacji znajdziesz w dokumentacji.
Powtarzanie animacji
Aby stale powtarzać animację, użyj funkcji rememberInfiniteTransition
i infiniteRepeatable
animationSpec
. Zmień atrybut RepeatModes
, aby określić sposób przechodzenia tam i z powrotem.
Aby powtarzać określoną liczbę razy, użyj funkcji finiteRepeatable
.
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 }
Rozpoczynanie animacji przy uruchamianiu funkcji kompozycyjnej
LaunchedEffect
jest uruchamiany, gdy do kompozycji wchodzi funkcja kompozycyjna. Uruchamia animację po uruchomieniu funkcji kompozycyjnej. Możesz ją wykorzystać do określenia zmiany stanu animacji. Użycie polecenia Animatable
z metodą animateTo
do uruchamiania animacji przy uruchomieniu:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Tworzenie animacji sekwencyjnych
Użycie interfejsów API współbieżnych Animatable
do wyświetlania animacji sekwencyjnych lub równoczesnych. Wywołanie animateTo
Animatable
po kolei sprawia, że każda animacja
czeka na zakończenie poprzednich animacji, zanim będzie można kontynuować .
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
Aby uzyskać równoczesne animacje, użyj współbieżnych interfejsów API (Animatable#animateTo()
lub animate
) albo interfejsu API Transition
. Jeśli używasz wielu funkcji uruchamiania w kontekście współużytkowania, są one uruchamiane razem z animacjami:
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 API updateTransition
, aby używać tego samego stanu do jednoczesnego uruchamiania wielu różnych animacji właściwości. W przykładzie poniżej animujemy 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 wydajności animacji
Animacje w funkcji tworzenia mogą powodować problemy z wydajnością. Wynika to z natury animacji: szybkiego ruchu lub zmiany pikseli na ekranie, klatka po klatce, tworząc iluzję ruchu.
Poznaj różne fazy tworzenia wiadomości: kompozycję, układ i rysowanie. Jeśli animacja zmieni fazę układu, wszystkie elementy kompozycyjne, których dotyczy problem, muszą przekazać układ i przerysować. Jeśli animacja jest w fazie rysowania, domyślnie jest skuteczniejsza niż w przypadku wyświetlania w wersji z układem, ponieważ jej praca wymaga mniej pracy.
Aby aplikacja działała w jak najmniejszym stopniu podczas animowania, w miarę możliwości wybierz wersję lambda interfejsu Modifier
. Spowoduje to pominięcie zmiany kompozycji i wykona animację poza fazą kompozycji. W przeciwnym razie użyj funkcji Modifier.graphicsLayer{ }
, ponieważ ten modyfikator zawsze działa na etapie rysowania. Więcej informacji na ten temat znajdziesz w sekcji na temat wstrzymywania odczytów w dokumentacji wydajności.
Zmiana czasu animacji
Funkcja tworzenia domyślnie używa w przypadku większości animacji animacji sprężyny. Sprężyny czy animacje
oparte na fizyce są bardziej naturalne. Są również przerywane, ponieważ
uwzględniają prędkość obiektu, a nie stały czas.
Jeśli chcesz zastąpić ustawienia domyślne, wszystkie interfejsy API animacji przedstawione powyżej mogą skonfigurować animationSpec
w celu dostosowania sposobu uruchamiania animacji, niezależnie od tego, czy ma ona być wykonywana przez określony czas, czy też ma być wykonywana w większym stopniu.
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 oparta na czasie trwania, która wyświetla animację między 2 wartościami za pomocą funkcjiEasing
.keyframes
: specyfikacja określania wartości w określonych kluczowych momentach animacji.repeatable
: specyfikacja oparta na czasie trwania, która jest uruchamiana określoną liczbę razy. Parametr ten określa parametrRepeatMode
.infiniteRepeatable
: specyfikacja zależna od czasu trwania działająca w nieskończoność.snap
: powoduje natychmiastowe przyciąganie do wartości końcowej bez animacji.
Aby dowiedzieć się więcej o parametrze animationSpecs, przeczytaj pełną dokumentację.
Dodatkowe materiały
Więcej przykładów zabawnych animacji w funkcji Utwórz znajdziesz w tych artykułach:
- 5 szybkich animacji w funkcji Utwórz
- Poruszanie meduz w komponencie
- Dostosowywanie pola
AnimatedContent
w sekcji Utwórz - Zawężenie funkcji wygładzania w obszarze tworzenia wiadomości