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 به شما این امکان را می دهد که اندازه اجزای سازنده را به چند روش مختلف متحرک کنید. از 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
برای متحرک کردن موقعیت یک 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 } )
اگر میخواهید هنگام متحرک کردن موقعیت یا اندازه، مطمئن شوید که Composableها روی یا زیر دیگر composableها کشیده نمیشوند، از 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) ) }
متحرک کردن بالشتک از یک 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 } )
متحرک کردن ارتفاع یک ترکیب پذیر
برای متحرک سازی ارتفاع یک 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
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
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
بخوانید.
در حین حرکت به مقاصد مختلف متحرک شوید
برای متحرک سازی انتقال بین اجزای سازنده هنگام استفاده از مصنوع ناوبری-نوشتن ، 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
با animationSpec
infiniteRepeatable
برای تکرار مداوم انیمیشن خود استفاده کنید. 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 }
شروع یک انیمیشن در راه اندازی یک composable
LaunchedEffect
زمانی اجرا می شود که یک composable وارد ترکیب شود. با راهاندازی یک کامپوزیشن، یک انیمیشن را شروع میکند، میتوانید از آن برای تغییر حالت انیمیشن استفاده کنید. استفاده از 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)) }
ایجاد انیمیشن های همزمان
برای دستیابی به انیمیشنهای همزمان از APIهای اصلی ( Animatable#animateTo()
یا animate
) یا Transition
API استفاده کنید. اگر از چندین تابع راه اندازی در یک زمینه معمولی استفاده کنید، انیمیشن ها را همزمان راه اندازی می کند:
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 می توانند مشکلات عملکردی ایجاد کنند. این به دلیل ماهیت انیمیشن است: حرکت یا تغییر سریع پیکسل ها روی صفحه، فریم به فریم برای ایجاد توهم حرکت.
مراحل مختلف Compose را در نظر بگیرید: ترکیب، چیدمان و ترسیم. اگر انیمیشن شما فاز طرحبندی را تغییر دهد، به همه اجزای سازنده آسیبدیده نیاز دارد که به نمایش گذاشته و دوباره ترسیم شوند. اگر انیمیشن شما در مرحله قرعه کشی اتفاق بیفتد، به طور پیش فرض عملکرد بیشتری نسبت به زمانی دارد که انیمیشن را در مرحله طرح بندی اجرا کنید، زیرا به طور کلی کار کمتری برای انجام دادن دارد.
برای اطمینان از اینکه برنامه شما در حین پویانمایی کمترین کار ممکن را انجام می دهد، در صورت امکان نسخه لامبدا یک Modifier
را انتخاب کنید. این کار از ترکیب مجدد صرفنظر می کند و انیمیشن را خارج از مرحله ترکیب بندی انجام می دهد، در غیر این صورت از Modifier.graphicsLayer{ }
استفاده کنید، زیرا این اصلاح کننده همیشه در مرحله ترسیم اجرا می شود. برای اطلاعات بیشتر در این مورد، به بخش خواندن به تعویق در اسناد عملکرد مراجعه کنید.
تغییر زمان انیمیشن
Compose به طور پیش فرض از انیمیشن های فنری برای اکثر انیمیشن ها استفاده می کند. فنرها یا انیمیشن های مبتنی بر فیزیک، طبیعی تر هستند. آنها همچنین قابل وقفه هستند زیرا به جای زمان ثابت، سرعت فعلی جسم را در نظر می گیرند. اگر میخواهید پیشفرض را لغو کنید، همه APIهای انیمیشنی که در بالا نشان داده شدهاند، این توانایی را دارند که animationSpec
را برای سفارشی کردن نحوه اجرای یک انیمیشن تنظیم کنند، چه بخواهید در مدت زمان معینی اجرا شود یا بیشتر پرشور باشد.
در زیر خلاصه ای از گزینه های مختلف animationSpec
آمده است:
-
spring
: انیمیشن مبتنی بر فیزیک، پیشفرض برای همه انیمیشنها. میتوانید سفتی یا نسبت میرایی را تغییر دهید تا به ظاهر و احساس انیمیشن متفاوتی برسید. -
tween
(مخفف بین ): انیمیشن مبتنی بر مدت زمان، بین دو مقدار با تابعEasing
متحرک می شود. -
keyframes
: مشخصاتی برای تعیین مقادیر در نقاط کلیدی خاصی در یک انیمیشن. -
repeatable
: مشخصات مبتنی بر مدت زمان که تعداد معینی بار اجرا می شود، مشخص شده توسطRepeatMode
. -
infiniteRepeatable
: مشخصات مبتنی بر مدت زمان که برای همیشه اجرا می شود. -
snap
: فوراً بدون هیچ گونه انیمیشنی به مقدار نهایی میچسبد.
برای اطلاعات بیشتر در مورد animationSpecs، مستندات کامل را بخوانید.
منابع اضافی
برای نمونههای بیشتر از انیمیشنهای سرگرمکننده در Compose، به موارد زیر نگاهی بیندازید:
- 5 انیمیشن سریع در Compose
- حرکت چتر دریایی در Compose
- سفارشی کردن
AnimatedContent
در نوشتن - Easing به Easing توابع در Compose