Compose سازوکارهای پویانمایی داخلی زیادی دارد و ممکن است انتخاب اینکه از کدام استفاده کنید گیجکننده باشد. در زیر فهرستی از موارد استفاده رایج از پویانمایی آمده است. برای کسب اطلاعات دقیقتر درباره مجموعه کامل گزینههای مختلف API دسترسیپذیر برای شما، مستندات کامل «ساختن پویانمایی» را بخوانید.
پویانمایی کردن داراییهای ترکیبی مشترک
Compose میاناهای برنامهسازی کاربردی مناسبی ارائه میدهد که به شما امکان میدهد بسیاری از موارد استفاده رایج پویانمایی را حل کنید. این بخش نشان میدهد چگونه میتوانید ویژگیهای معمول یک عنصر ترکیبی را پویانمایی کنید.
پویانمایی ظاهر شدن / ناپدید شدن
از 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 به شما امکان میدهند نحوه عملکرد
یک عنصر ترکیبی را هنگام ظاهر و ناپدید شدن پیکربندی کنید. برای کسب اطلاعات بیشتر، اسناد
کامل را بخوانید.
گزینه دیگر برای پویانمایی کردن نمایان بودن یک عنصر ترکیبی، پویانمایی کردن
آلفا در طول زمان بااستفاده از 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) ) }
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 ترکیبشدنی استفاده کنید:
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 }, // ... )
جابهجا شدن بین انواع مختلف محتوا
اگر فقط محو شدن استاندارد بین عناصر ترکیبی را میخواهید، از Crossfade استفاده کنید. برای پویانمایی بین عناصر ترکیبی مختلف، از AnimatedContent استفاده کنید.
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 عنصر، 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 }
شروع پویانمایی در زمان راهاندازی عنصر ترکیبی
LaunchedEffect وقتی اجرا میشود که عنصر ترکیبی وارد ترکیببندی شود. با راهاندازی عنصر ترکیبی، پویانمایی شروع میشود، میتوانید از این برای هدایت تغییر وضعیت پویانمایی استفاده کنید. استفاده از Animatable با روش animateTo برای شروع
پویانمایی در زمان راهاندازی:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
ساختن پویانماییهای ترتیبی
از APIهای روتین فرعی 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)) }
ساختن پویانماییهای همزمان
برای دستیابی به پویانماییهای همزمان، از میاناهای برنامهسازی کاربردی روتین همکار (Animatable#animateTo() یا animate) یا
از میانای برنامهسازی کاربردی Transition استفاده کنید. اگر از چندین تابع
راهاندازی در زمینه روتین همکار استفاده کنید، پویانماییها را بهطور همزمان
راهاندازی میکند:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
میتوانید از updateTransition API برای استفاده از وضعیت یکسان برای هدایت
همزمان بسیاری از پویانماییهای مختلف دارایی استفاده کنید. مثال زیر
دو دارایی را که با تغییر وضعیت کنترل میشوند، 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 میتوانند باعث مشکلات عملکرد شوند. این امر بهدلیل ماهیت پویانمایی است: پویانمایی عبارت است از حرکت یا تغییر سریع پیکسلها روی صفحه، قاببهقاب، برای ایجاد توهم حرکت.
مراحل مختلف «ترکیب» را درنظر بگیرید: ترکیب، چیدمان، و طراحی. اگر پویانمایی شما فاز چیدمان را تغییر دهد، همه عناصر ترکیبی تحتتأثیر قرارگرفته باید دوباره چیده و دوباره رسم شوند. اگر پویانمایی شما در مرحله رسم رخ دهد، بهطور پیشفرض عملکرد بهتری نسبتبه زمانی دارد که پویانمایی را در مرحله چیدمان اجرا کنید، زیرا در این حالت کار کمتری برای انجام دادن دارد.
برای اینکه مطمئن شوید برنامه شما درحین پویانمایی تا حد امکان کار کمی انجام میدهد، درصورت امکان نسخه لامبدای Modifier را انتخاب کنید. این کار باعث میشود بازآرایی رد شود و پویانمایی
خارج از مرحله ترکیب انجام شود، درغیراینصورت از
Modifier.graphicsLayer{ } استفاده کنید، زیرا این اصلاحگر همیشه در مرحله
کشیدن اجرا میشود. برای کسب اطلاعات بیشتر درباره این موضوع، به بخش بهتعویق انداختن خواندنها در
اسناد عملکرد مراجعه کنید.
تغییر زمانبندی پویانمایی
Compose بهطور پیشفرض از پویانماییهای فنری برای اکثر پویانماییها استفاده میکند. فنرها یا
پویانماییهای مبتنی بر فیزیک، طبیعیتر بهنظر میرسند. این حرکتها همچنین قابلوقفه هستند زیرا بهجای زمان ثابت، سرعت فعلی شیء را درنظر میگیرند.
اگر میخواهید پیشفرض را ملغی کنید، همه APIهای پویانمایی که قبلاً نشان داده شدهاند قابلیت تنظیم animationSpec را دارند تا نحوه اجرای پویانمایی را سفارشیسازی کنند، چه بخواهید پویانمایی در مدت زمان معینی اجرا شود یا جهشیتر باشد.
در زیر خلاصهای از گزینههای مختلف animationSpec آمده است:
spring: پویانمایی مبتنی بر فیزیک، پیشفرض برای همه پویانماییها. میتوانید سختی یا نسبت میرایی را تغییر دهید تا به پویانمایی با ظاهر و احساسی متفاوت دست یابید.tween(کوتهنوشت بین): پویانمایی مبتنی بر مدت، بین دو مقدار با تابعEasingپویانمایی میکند.keyframes: مشخصات برای تعیین مقادیر در نقاط کلیدی خاص در یک پویانمایی.repeatable: مشخصات مبتنی بر مدت که تعداد مشخصی بار اجرا میشود، که توسطRepeatModeمشخص شده است.infiniteRepeatable: مشخصات مبتنی بر مدت که برای همیشه اجرا میشود.snap: بدون هیچ پویانمایی، فوراً به مقدار نهایی میچسبد.
برای اطلاعات بیشتر درباره animationSpecs، مستندات کامل را بخوانید.
منابع بیشتر
برای دیدن نمونههای بیشتر از پویانماییهای جالب در «نوشتن»، به موارد زیر نگاهی بیندازید:
- ۵ پویانمایی سریع در «نوشتن»
- حرکت دادن عروس دریایی در «نوشتن»
- سفارشیسازی
AnimatedContentدر «نوشتن» - آشنایی با توابع «آسانسازی» در «نوشتن»