Compose مکانیزمهای انیمیشن داخلی زیادی دارد و انتخاب یکی از آنها میتواند دشوار باشد. در زیر لیستی از موارد استفاده رایج از انیمیشن آمده است. برای اطلاعات بیشتر در مورد مجموعه کامل گزینههای مختلف API موجود، مستندات کامل Compose Animation را مطالعه کنید.
متحرکسازی ویژگیهای ترکیبی رایج
Compose رابطهای برنامهنویسی کاربردی (API) مناسبی ارائه میدهد که به شما امکان میدهد بسیاری از موارد استفاده رایج از انیمیشن را حل کنید. این بخش نشان میدهد که چگونه میتوانید ویژگیهای رایج یک composable را متحرکسازی کنید.
متحرک سازی ظاهر شدن/ناپدید شدن

 از AnimatedVisibility برای پنهان کردن یا نمایش یک Composable استفاده کنید. فرزندان درون 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 به شما امکان میدهند نحوه رفتار یک composable را هنگام ظاهر شدن و ناپدید شدن پیکربندی کنید. برای اطلاعات بیشتر، مستندات کامل را مطالعه کنید.
 گزینه دیگر برای متحرکسازی قابلیت مشاهده یک composable، متحرکسازی آلفا در طول زمان با استفاده از 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 به شما امکان میدهد اندازه composableها را به چند روش مختلف متحرک کنید. برای انیمیشن بین تغییرات اندازه composable از 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 برای توصیف نحوهی اعمال تغییرات اندازه استفاده کنید.
متحرک سازی موقعیت ترکیب پذیر

 برای متحرکسازی موقعیت یک composable، از 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 } )
 اگر میخواهید مطمئن شوید که هنگام متحرکسازی موقعیت یا اندازه، عناصر ترکیبی (composables) روی یا زیر عناصر ترکیبی دیگر کشیده نمیشوند، از 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) ) }

Modifier.layout{ }متحرکسازی padding یک composable

 برای متحرکسازی padding یک composable، از 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 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) ) { }
 روش دیگر، استفاده از Card composable و تنظیم ویژگی elevation روی مقادیر مختلف در هر حالت است.
متحرکسازی مقیاس، ترجمه یا چرخش متن

 هنگام متحرکسازی مقیاس، انتقال یا چرخش متن، پارامتر 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) ) }
رنگ متن را متحرک کنید

 برای متحرکسازی رنگ متن، از لامبدا color در کامپوننت 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 برای متحرکسازی بین ترکیبهای مختلف استفاده کنید، اگر فقط میخواهید محوشدگی استاندارد بین ترکیبهای مختلف وجود داشته باشد، از 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 میتوان طوری تنظیم کرد که انواع مختلفی از انتقالهای ورود و خروج را نشان دهد. برای اطلاعات بیشتر، مستندات مربوط به AnimatedContent یا این پست وبلاگ در مورد AnimatedContent مطالعه کنید.
متحرکسازی هنگام پیمایش به مقاصد مختلف

 برای متحرکسازی انتقال بین کامپوننتها هنگام استفاده از navigation-compose artifact، enterTransition و exitTransition را روی یک کامپوننت مشخص کنید. همچنین میتوانید انیمیشن پیشفرض را برای استفاده برای همه مقصدها در 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 تغییر دهید تا نحوه رفت و برگشت آن را مشخص کنید.
 از repeatable برای تکرار به تعداد دفعات مشخص استفاده کنید. 
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 }
شروع یک انیمیشن با اجرای یک composable
 LaunchedEffect زمانی اجرا میشود که یک composable وارد کامپوزیشن شود. این تابع با اجرای یک composable، یک انیمیشن را شروع میکند، میتوانید از این برای تغییر حالت انیمیشن استفاده کنید. استفاده از Animatable با متد animateTo برای شروع انیمیشن در هنگام اجرا: 
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
ایجاد انیمیشنهای متوالی

 از APIهای کوروتین Animatable برای اجرای انیمیشنهای متوالی یا همزمان استفاده کنید. فراخوانی animateTo روی Animatable یکی پس از دیگری باعث میشود هر انیمیشن قبل از ادامه منتظر پایان انیمیشنهای قبلی بماند. دلیل این امر این است که این یک تابع suspend است. 
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های کوروتین ( 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 برای استفاده از یک state یکسان جهت اجرای انیمیشنهای مختلف برای ویژگیهای مختلف به طور همزمان استفاده کنید. مثال زیر دو ویژگی را که توسط یک state به نام change کنترل میشوند، متحرکسازی میکند: 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 میتوانند باعث مشکلات عملکردی شوند. این به دلیل ماهیت انیمیشن است: حرکت یا تغییر سریع پیکسلها روی صفحه، فریم به فریم برای ایجاد توهم حرکت.
مراحل مختلف Compose را در نظر بگیرید: ترکیببندی، طرحبندی و رسم. اگر انیمیشن شما مرحله طرحبندی را تغییر دهد، لازم است همه Composableهای تحت تأثیر قرار گرفته، رله و رسم مجدد شوند. اگر انیمیشن شما در مرحله رسم رخ دهد، به طور پیشفرض عملکرد بیشتری نسبت به زمانی دارد که انیمیشن را در مرحله طرحبندی اجرا میکنید، زیرا در کل کار کمتری برای انجام دادن خواهد داشت.
 برای اطمینان از اینکه برنامه شما هنگام انیمیشنسازی تا حد امکان کمترین کار را انجام میدهد، در صورت امکان نسخه لامبدا از یک Modifier انتخاب کنید. این کار از ترکیب مجدد صرفنظر میکند و انیمیشن را خارج از مرحله ترکیب اجرا میکند، در غیر این صورت از Modifier.graphicsLayer{ } استفاده کنید، زیرا این اصلاحکننده همیشه در مرحله ترسیم اجرا میشود. برای اطلاعات بیشتر در این مورد، به بخش تعویق خواندن در مستندات عملکرد مراجعه کنید.
تغییر زمان انیمیشن
 Compose به طور پیشفرض از انیمیشنهای فنری برای اکثر انیمیشنها استفاده میکند. فنرها یا انیمیشنهای مبتنی بر فیزیک، طبیعیتر به نظر میرسند. آنها همچنین قابل وقفه هستند زیرا به جای یک زمان ثابت، سرعت فعلی شیء را در نظر میگیرند. اگر میخواهید پیشفرض را نادیده بگیرید، تمام APIهای انیمیشن نشان داده شده در بالا قابلیت تنظیم animationSpec را برای سفارشیسازی نحوه اجرای یک انیمیشن دارند، چه بخواهید در مدت زمان مشخصی اجرا شود یا بیشتر حالت فنری داشته باشد.
 در زیر خلاصهای از گزینههای مختلف animationSpec آمده است:
-  spring: انیمیشن مبتنی بر فیزیک، پیشفرض برای همه انیمیشنها. میتوانید سختی یا نسبت میرایی را تغییر دهید تا به ظاهر و حس انیمیشن متفاوتی دست یابید.
-  tween(مخفف between ): انیمیشن مبتنی بر مدت زمان، انیمیشنها را بین دو مقدار با استفاده از یک تابعEasingانجام میدهد.
-  keyframes: مشخصههایی برای تعیین مقادیر در نقاط کلیدی خاص در یک انیمیشن.
-  repeatable: مشخصات مبتنی بر مدت زمان که تعداد دفعات مشخصی اجرا میشود و توسطRepeatModeمشخص میشود.
-  infiniteRepeatable: مشخصات مبتنی بر مدت زمان که برای همیشه اجرا میشود.
-  snap: فوراً و بدون هیچ انیمیشنی به مقدار پایانی میچسبد.

برای اطلاعات بیشتر در مورد animationSpecs، مستندات کامل را مطالعه کنید.
منابع اضافی
برای مثالهای بیشتر از انیمیشنهای سرگرمکننده در Compose، به موارد زیر نگاهی بیندازید:
- ۵ انیمیشن سریع در Compose
- حرکت دادن عروس دریایی در Compose
-  سفارشیسازی AnimatedContentدر Compose
- تسهیل در توابع تسهیل در Compose
