לכתיבה יש הרבה מנגנוני אנימציה מובנים, אנחנו נדע באיזה מהם לבחור. בהמשך מפורטים תרחישים נפוצים לדוגמה לשימוש באנימציה. למידע מפורט יותר על כל אפשרויות ה-API הזמינות, קראו את מסמכי התיעוד המלאים של Compose Animation.
יוצרים אנימציה של תכונות קומפוזביליות נפוצות
Compose מספק ממשקי API נוחים שמאפשרים לכם לפתור הרבה תרחישים נפוצים לדוגמה של אנימציה. בקטע הזה נסביר איך להוסיף אנימציה למאפיינים נפוצים של רכיב מורכב.
הצגת אנימציה / נעלמת
אפשר להשתמש ב-AnimatedVisibility
כדי להסתיר או להציג תוכן קומפוזבילי. ילדים בתוך
AnimatedVisibility
יכול/ה להשתמש בשדה Modifier.animateEnterExit()
להזנה משלו
או לצאת מהמעבר.
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 // ... }
הפרמטרים לכניסה וליציאה של AnimatedVisibility
מאפשרים להגדיר איך
תוכן קומפוזבילי פועל כשהוא מופיע ונעלם. מידע נוסף זמין במסמכי העזר המלאים.
אפשרות נוספת לאנימציה של הניראות של תוכן קומפוזבילי היא להוסיף אנימציה
alpha לאורך זמן באמצעות 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) ) { }
עם זאת, שינוי הערך של אלפא כרוך בזהירות: הרכיב הניתן ליצירה נשאר בהרכב וממשיך לתפוס את המרחב שבו הוא מופיע. כתוצאה מכך, קוראי מסך ומנגנוני נגישות אחרים עדיין עשויים להתייחס לפריט במסך. לעומת זאת, AnimatedVisibility
מסיר את הפריט מההרכב בסופו של דבר.
אנימציית צבע רקע
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
האפשרות הזו מניבה ביצועים טובים יותר בהשוואה לשימוש ב-Modifier.background()
.
אפשר להשתמש ב-Modifier.background()
להגדרת צבע של תמונה אחת, אבל
אם אתם מוסיפים אנימציה של צבע לאורך זמן, זה עלול לגרום להרכבת מחדש
הנחוצים.
כדי ליצור אנימציה של צבע הרקע בלי סוף, אפשר לעיין בקטע חזרה על קטע אנימציה.
אנימציה של הגודל של רכיב מורכב
ב-Compose אפשר להוסיף אנימציה לגודל של רכיבים מורכבים בכמה דרכים. משתמשים ב-animateContentSize()
כדי ליצור אנימציות בין שינויי גודל של רכיבים שאפשר לשלב.
לדוגמה, אם יש לך תיבה שמכילה טקסט שיכול להתרחב מ
אפשר להשתמש ב-Modifier.animateContentSize()
כדי ליצור תנועה חלקה יותר
מעבר:
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 } ) { }
אפשר גם להשתמש ב-AnimatedContent
עם SizeTransform
כדי לתאר
האופן שבו אמורים להתרחש שינויים בגודל.
הוספת אנימציה למיקום של תוכן קומפוזבילי
כדי להנפיש את המיקום של רכיב מורכב, משתמשים ב-Modifier.offset{ }
בשילוב עם 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 } )
אם אתם רוצים לוודא שתכנים קומפוזביליים לא ייכתבו על פני תוכן אחר או לא
תכנים קומפוזביליים כשמוסיפים אנימציה של מיקום או גודל, צריך להשתמש ב-Modifier.layout{ }
. הזה
מפיצ' את השינויים בגודל ובמיקום של ההורה, שמשפיע על
לילדים אחרים.
לדוגמה, אם מעבירים Box
בתוך Column
והילדים האחרים צריכים לזוז כש-Box
זז, צריך לכלול את פרטי ההיסט ב-Modifier.layout{ }
באופן הבא:
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) ) }
אנימציה של הרווח של רכיב מורכב
כדי ליצור אנימציה של הרווח של רכיב מורכב, משתמשים ב-animateDpAsState
בשילוב עם 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 } )
אנימציה של הגובה של תוכן קומפוזבילי
כדי להוסיף אנימציה להעלאה של תוכן קומפוזבילי, צריך להשתמש ב-animateDpAsState
בשילוב עם
Modifier.graphicsLayer{ }
. לשינויי גובה חד-פעמיים, משתמשים ב-Modifier.shadow()
. אם אתם יוצרים אנימציה של הצל, השימוש במשתנה 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) ) { }
לחלופין, אפשר להשתמש ברכיב ה-composable Card
ולהגדיר את נכס הגובה לערכי שונים לכל מצב.
הוספת אנימציה לשינוי קנה המידה, התרגום או הסיבוב של הטקסט
כשמפעילים אנימציה של שינוי קנה המידה, התרגום או הסיבוב של הטקסט, מגדירים את הפרמטר textMotion
ב-TextStyle
לערך TextMotion.Animated
. כך אפשר להבטיח מעברים חלקים יותר בין אנימציות טקסט. שימוש ב-Modifier.graphicsLayer{ }
כדי
לתרגם, לסובב או לשנות את קנה המידה של הטקסט.
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) ) }
אנימציה של צבע הטקסט
כדי ליצור אנימציה של צבע הטקסט, משתמשים ב-lambda color
ב-composable 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 }, // ... )
מעבר בין סוגי תוכן שונים
משתמשים ב-AnimatedContent
כדי להוסיף אנימציה למעבר בין רכיבים שונים של Composables. אם רוצים רק מעבר סטנדרטי בין רכיבים של Composables, משתמשים ב-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
כדי להציג סוגים רבים ושונים של Enter
מעברי יציאה. מידע נוסף זמין במסמכי העזרה של AnimatedContent
או בפוסט הזה בבלוג בנושא AnimatedContent
.
אנימציה בזמן ניווט ליעדים שונים
כדי להוסיף אנימציה למעברים בין רכיבי composable כשמשתמשים באובייקט navigation-compose, צריך לציין את הערכים enterTransition
ו-exitTransition
ברכיב composable. אפשר גם להגדיר את אנימציית ברירת המחדל שתהיה רלוונטית לכל היעדים ברמה העליונה 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( // ... ) } }
יש הרבה סוגים שונים של מעברים בין כניסה ויציאה הרלוונטיים על התוכן הנכנס והיוצא, תיעוד.
איך חוזרים על אנימציה
כדי לחזור על האנימציה באופן קבוע, משתמשים ב-rememberInfiniteTransition
עם infiniteRepeatable
animationSpec
. שינוי של RepeatModes
ל-
ולציין איך היא צריכה לפעול הלוך ושוב.
משתמשים ב-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 }
הפעלת אנימציה בזמן ההפעלה של רכיב מורכב
הפונקציה LaunchedEffect
פועלת כשתוכן קומפוזבילי נכנס ליצירה. מתחיל
אנימציה בהשקה של תוכן קומפוזבילי, אפשר להשתמש בו כדי להגביר את האנימציה
שינוי במצב מסוים. משתמשים ב-Animatable
עם השיטה animateTo
כדי להתחיל את
אנימציה בהפעלה:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
יצירת אנימציות רצופות
שימוש בממשקי ה-API של coroutine Animatable
כדי לבצע פעולות ברצף או בו-זמנית
אנימציות. קריאה ל-animateTo
על ה-Animatable
בזה אחר זה גורמת לכל אנימציה להמתין לסיום האנימציות הקודמות לפני שהיא ממשיכה.
הסיבה לכך היא שזו פונקציית השהיה.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
יצירת אנימציות בו-זמנית
להשתמש בממשקי API של Coroutine (Animatable#animateTo()
או animate
), או
את ה-API של Transition
כדי ליצור אנימציות בו-זמנית. אם משתמשים בכמה פונקציות הפעלה בהקשר של קורוטין, האנימציות יופעלו בו-זמנית:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
אפשר להשתמש ב-API של updateTransition
כדי להשתמש באותו מצב כדי להפעיל בו-זמנית אנימציות רבות ושונות של נכסים. הדוגמה הבאה מכילה אנימציה
שני נכסים שנשלטים על ידי שינוי מצב, rect
ו-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 } }
אופטימיזציה של ביצועי האנימציה
אנימציות ב-Compose עלולות לגרום לבעיות בביצועים. הסיבה לכך היא מהות האנימציה: תנועה או שינוי של פיקסלים במסך במהירות, פריים אחרי פריים, כדי ליצור את האשליה של תנועה.
כדאי להביא בחשבון את השלבים השונים של כתיבה: הרכבה, פריסה וציור. אם המיקום האנימציה משנה את שלב הפריסה, היא דורשת שכל התכנים הקומפוזביליים המושפעים ולכתוב מחדש. אם האנימציה מתרחשת בשלב השרטוט, היא יניבו ביצועים טובים יותר מאשר אם תריצו את האנימציה בפריסה שלב זה, כי הדבר היה מצריך פחות עבודה באופן כללי.
כדי לוודא שהאפליקציה תבצע מעט ככל האפשר כשמפעילים אנימציה, כדאי לבחור ב-lambda
של Modifier
כשהדבר אפשרי. פעולה זו מדלגת על הרכבה מחדש ומבצעת
את האנימציה שמחוץ לשלב היצירה. אחרת, השתמשו
Modifier.graphicsLayer{ }
, מאחר שמאפיין הצירוף הזה תמיד פועל ב-Drawings
שלב אחד. מידע נוסף זמין בקטע דחיית קריאות במסמכי העזרה בנושא ביצועים.
שינוי תזמון האנימציה
כברירת מחדל, ב-Compose נעשה שימוש באנימציות קפיצה ברוב האנימציות. אביבים, או
אנימציות שמבוססות על פיזיקה, מרגישות יותר טבעיות. הם גם ניתנים להפרעה, כי הם מביאים בחשבון את המהירות הנוכחית של האובייקט במקום זמן קבוע.
אם רוצים לשנות את ברירת המחדל, אפשר להשתמש ב-animationSpec
בכל ממשקי ה-API של האנימציה שצוינו למעלה כדי להתאים אישית את אופן ההפעלה של האנימציה, למשל, להגדיר שהיא תפעל למשך זמן מסוים או שתהיה יותר קופצנית.
בהמשך מופיע סיכום של האפשרויות השונות של animationSpec
:
spring
: אנימציה מבוססת פיזיקה, שמוגדרת כברירת המחדל לכל האנימציות. אפשר לשנות את הערך של stiffness או dampingRatio כדי לקבל מראה ותחושה שונים של האנימציה.tween
(קיצור של between): אנימציה שמבוססת על משך זמן, שמציגה אנימציה בין שני ערכים באמצעות פונקצייתEasing
.keyframes
: מפרט לציון ערכים בנקודות מפתח מסוימות באנימציה.repeatable
: מפרט שמבוסס על משך זמן, שפועל מספר מסוים של פעמים, כפי שמצוין ב-RepeatMode
.infiniteRepeatable
: מפרט שמבוסס על משך זמן ופועל לנצח.snap
: הצמדה מיידית לערך הסופי ללא אנימציה.
מידע נוסף על animationSpecs זמין במסמכי התיעוד המלאים.
מקורות מידע נוספים
דוגמאות נוספות לאנימציות משעשעות חדשניות ב'כתיבה':
- 5 אנימציות מהירות ב'כתיבה'
- איך גורמים ל-Jellyfish לזוז ב-Compose
- התאמה אישית של
AnimatedContent
ב-Compose - התאמה לפונקציות של הקלה בכתיבה