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 برای توصیف
نحوه تغییر اندازه استفاده کنید.
پویانمایی کردن موقعیت ترکیبپذیر
برای پویانمایی کردن موقعیت یک ترکیبپذیر، از 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 } )
پویانمایی ارتفاع یک ترکیبپذیر
برای پویانمایی کردن ارتفاع یک ترکیبپذیر، از 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 استفاده کنید تا پویانماییتان بهطور مداوم تکرار شود. 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 } )
ایجاد پویانماییهای متوالی
از میاناهای برنامهسازی کاربردی همروال 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) کوروتین (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 - آشنایی با توابع «آرامسازی» در Compose