Compose سازوکارهای پویانمایی داخلی زیادی دارد و دانستن اینکه کدامیک را انتخاب کنید میتواند دشوار باشد. در زیر فهرستی از موارد استفاده رایج از پویانمایی آمده است. برای اطلاعات دقیقتر درباره مجموعه کامل گزینههای مختلف API که برای شما دردسترس است، سند کامل Compose Animation را بخوانید.
پویانمایی کردن ویژگیهای ترکیبپذیر رایج
Compose میاناهای برنامهسازی کاربردی مناسبی ارائه میدهد که به شما امکان میدهد بسیاری از موارد استفاده رایج پویانمایی را حل کنید. این بخش نشان میدهد که چگونه میتوانید ویژگیهای رایج یک 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، پویانمایی کردن
آلفا درطول زمان بااستفاده از 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() برای تنظیم رنگ یکباره قابلقبول است، اما هنگام
پویانمایی رنگ درطول زمان، این کار میتواند باعث بازترکیبهای بیشتری نسبتبه
حد لازم شود.
برای پویانمایی بینهایت رنگ پسزمینه، به بخش تکرار پویانمایی مراجعه کنید.
Animate the size of a composable
Compose به شما امکان میدهد اندازه 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 } )
اگر میخواهید مطمئن شوید که ترکیبپذیرها هنگام پویانمایی موقعیت یا اندازه روی یا زیر ترکیبپذیرهای دیگر کشیده نمیشوند، از 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{ }پویانمایی کردن حاشیه داخلی ترکیبشدنی
برای پویانمایی کردن حاشیه داخلی یک ترکیبشدنی، از 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 } )
پویانمایی ارتفاع یک ترکیبپذیر
برای پویانمایی کردن ارتفاع یک composable، از 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) ) { }
همچنین میتوانید از ترکیبپذیر 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) ) }
پویانمایی رنگ نوشتار
برای پویانمایی رنگ نوشتار، از لامبدا color در BasicText composable استفاده کنید:
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 را بخوانید.
پویانمایی هنگام پیمایش به مقصدهای مختلف
برای پویانمایی انتقالها بین 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 استفاده کنید تا پویانماییتان بهطور مداوم تکرار شود. RepeatMode را تغییر دهید تا
مشخص کنید چطور باید عقب و جلو برود.
از 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 } )
ایجاد پویانماییهای متوالی
از میاناهای برنامهسازی کاربردی همروال 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) } }
میتوانید از میانای برنامهسازی کاربردی 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 میتوانند باعث بروز مشکلات عملکردی شوند. این بهدلیل ماهیت پویانمایی است: حرکت دادن یا تغییر دادن سریع پیکسلها در صفحهنمایش، فریمبهفریم برای ایجاد توهم حرکت.
فازهای مختلف Compose را درنظر بگیرید: ترکیببندی، چیدمان، و ترسیم. اگر پویانمایی شما فاز چیدمان را تغییر دهد، لازم است همه ترکیبشدنیهای تحتتأثیر قرارگرفته دوباره چیدمان و بازترسیم شوند. اگر پویانمایی شما در فاز ترسیم رخ دهد، بهطور پیشفرض عملکرد بهتری نسبت به زمانی دارد که پویانمایی را در فاز چیدمان اجرا کنید، زیرا در مجموع کار کمتری برای انجام دادن دارد.
برای اطمینان از اینکه برنامهتان درحین پویانمایی کمترین کار ممکن را انجام میدهد،
درصورت امکان نسخه لامبدا Modifier را انتخاب کنید. این کار ترکیببندی مجدد را رد میکند و پویانمایی را خارج از فاز ترکیببندی انجام میدهد، درغیراینصورت از Modifier.graphicsLayer{ } استفاده کنید، زیرا این اصلاحکننده همیشه در فاز ترسیم اجرا میشود. برای اطلاعات بیشتر درباره این موضوع، به بخش بهتعویق انداختن خواندن در
اسناد عملکرد مراجعه کنید.
تغییر دادن زمانبندی پویانمایی
Compose بهطور پیشفرض از پویانماییهای فنری برای اکثر پویانماییها استفاده میکند. پویانماییهای فنری یا فیزیکمحور طبیعیتر بهنظر میرسند. همچنین، این انتقالها قابلقطع شدن هستند زیرا
بهجای زمان ثابت، سرعت فعلی شیء را درنظر میگیرند.
اگر میخواهید مقدار پیشفرض را لغو کنید، همه میاناهای برنامهسازی کاربردی پویانمایی که قبلاً نشان داده شدند میتوانند animationSpec را تنظیم کنند تا نحوه اجرای پویانمایی را سفارشیسازی کنند، چه بخواهید در مدتزمان خاصی اجرا شود یا جهش بیشتری داشته باشد.
در زیر خلاصهای از گزینههای مختلف animationSpec آورده شده است:
spring: پویانمایی مبتنی بر فیزیک، پیشفرض برای همه پویانماییها. میتوانید سفتی یا dampingRatio را تغییر دهید تا ظاهر و حس پویانمایی متفاوتی داشته باشید.tween(کوتهنوشت between): پویانمایی مبتنی بر مدت، بین دو مقدار با تابعEasingپویانمایی میکند.keyframes: مشخصات برای تعیین مقادیر در نقاط کلیدی خاص در یک پویانمایی.repeatable: مشخصه مبتنی بر مدتزمان که تعداد دفعات مشخصی اجرا میشود، که باRepeatModeمشخص شده است.infiniteRepeatable: مشخصات مبتنی بر مدت زمان که برای همیشه اجرا میشود.snap: فوراً بدون هیچ پویانماییای به مقدار نهایی میرود.
برای اطلاعات بیشتر درباره animationSpecs، مستندات کامل را بخوانید.
منابع بیشتر
برای نمونههای بیشتر از پویانماییهای جالب در Compose، به موارد زیر نگاهی بیندازید:
- ۵ پویانمایی سریع در Compose
- حرکت دادن عروس دریایی در Compose
- سفارشیسازی
AnimatedContentدر Compose - Easing into Easing functions in Compose