يتضمّن Compose العديد من آليات الصور المتحركة المضمّنة، وقد يكون من الصعب معرفة أيّ منها يجب اختياره. فيما يلي قائمة بحالات استخدام الرسوم المتحركة الشائعة. لمزيد من المعلومات التفصيلية حول المجموعة الكاملة من خيارات واجهة برمجة التطبيقات المختلفة المتاحة لك، يمكنك قراءة مستندات Compose Animation الكاملة.
إضافة حركة إلى خصائص الدوال المركّبة الشائعة
توفّر 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.TopCenckGuide.kt
ومع ذلك، فإنّ تغيير قيمة ألفا يأتي مع تحذير بأنّ الدالة المركّبة تظل في التركيب وتستمر في شغل المساحة التي تم وضعها فيها. قد يؤدي ذلك إلى استمرار برامج قراءة الشاشة وآليات تسهيل الاستخدام الأخرى في اعتبار العنصر معروضًا على الشاشة. من ناحية أخرى، يزيل AnimatedVisibility في النهاية
العنصر من التكوين.
تحريك لون الخلفية
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composackGuide.kt
يوفّر هذا الخيار أداءً أفضل من استخدام 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 ckGuide.kt
إذا أردت التأكّد من عدم رسم الدوال المركّبة فوق أو تحت دوال مركّبة أخرى عند إضافة تأثيرات حركية إلى الموضع أو الحجم، استخدِم 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, con>straints - 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(colorBluckGuide.kt
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 ckGuide.kt
إضافة تأثيرات حركية إلى ارتفاع دالة مركّبة
لتحريك ارتفاع دالة مركّبة، استخدِم 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(colorGrckGuide.kt
يمكنك بدلاً من ذلك استخدام الدالة المركّبة 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 = ) } AnimationQuickGuide.kt
تحريك لون النص
لتحريك لون النص، استخدِم دالة lambda 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 = { ani// ... )AnimationQuickGuide.kt
التبديل بين أنواع مختلفة من المحتوى
استخدِم 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" ) { tar>getState - when (targetState) { UiState>.Loading - { LoadingScreen() } UiStat>e.Loaded - { LoadedScreen() } UiSta>te.Error - { ErrorScreen() ckGuide.kt
يمكن تخصيص 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 - ScreenDetai ) } }AnimationQuickGuide.kt
تتوفّر أنواع مختلفة من تأثيرات الانتقال عند الدخول والخروج، والتي تطبّق تأثيرات مختلفة على المحتوى الوارد والصادر. يمكنك الاطّلاع على المستندات لمزيد من المعلومات.
تكرار صورة متحركة
استخدِم 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) } ) { // yoimationQuickGuide.kt
بدء صورة متحركة عند تشغيل دالة مركّبة
يتم تشغيل LaunchedEffect عندما تدخل دالة مركّبة إلى التركيب. تبدأ هذه الدالة
حركة عند تشغيل دالة مركّبة، ويمكنك استخدامها لتغيير حالة
الحركة. استخدام Animatable مع طريقة animateTo لبدء
الرسوم المتحركة عند التشغيل:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
إنشاء صور متحركة متسلسلة
استخدِم واجهات برمجة التطبيقات Animatable coroutine لتنفيذ صور متحركة متسلسلة أو متزامنة. يؤدي استدعاء animateTo على Animatable واحدًا تلو الآخر إلى
انتظار كل حركة حتى تنتهي الحركات السابقة قبل المتابعة.
وذلك لأنّها دالة تعليق.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tweckGuide.kt
إنشاء صور متحركة متزامنة
استخدِم واجهات برمجة التطبيقات الخاصة بالروتين المشترك (Animatable#animateTo() أو animate) أو واجهة برمجة التطبيقات Transition لإنشاء صور متحركة متزامنة. إذا كنت تستخدم دوال تشغيل متعددة في سياق الكوروتين، فسيتم تشغيل الرسوم المتحركة في نفس الوقت:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100ckGuide.kt
يمكنك استخدام واجهة برمجة التطبيقات updateTransition لاستخدام الحالة نفسها لتشغيل
العديد من الصور المتحركة المختلفة للسمات في الوقت نفسه. يوضّح المثال التالي
كيفية تحريك خاصيتين يتم التحكّم فيهما من خلال تغيير الحالة، وهما rect وborderWidth:
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "transition") val rect by transition.animateRect(label = "r>ect") { state - when (state) { >BoxState.Collapsed - Rect(0f, 0f, 100f, 100f) > BoxState.Expanded - Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = &quo>t;borderWidth") { state - when (state) >{ BoxState.Collapsed - 1.>dp Bo } }AnimationQuickGuide.kt
تحسين أداء الصور المتحركة
يمكن أن تؤدي الصور المتحركة في Compose إلى حدوث مشاكل في الأداء. ويرجع ذلك إلى طبيعة الرسوم المتحركة: فهي عبارة عن وحدات بكسل تتحرك أو تتغير على الشاشة بسرعة، إطارًا تلو الآخر، لخلق وهم الحركة.
ضَع في اعتبارك المراحل المختلفة في Compose: الإنشاء والتنسيق والرسم. إذا كانت الصورة المتحركة تغيّر مرحلة التنسيق، يتطلب ذلك إعادة تنسيق وإعادة رسم جميع العناصر المركّبة المتأثرة. إذا حدثت الحركة في مرحلة الرسم، فإنها تكون افتراضيًا أكثر أداءً مما لو كنت ستقوم بتشغيل الحركة في مرحلة التخطيط ، حيث سيكون لديها عمل أقل للقيام به بشكل عام.
لضمان أن يؤدي تطبيقك أقل قدر ممكن من المهام أثناء عرض الصور المتحركة، اختَر إصدار lambda من Modifier حيثما أمكن. يؤدي ذلك إلى تخطّي إعادة الإنشاء وتنفيذ
الحركة خارج مرحلة الإنشاء، وإلا يمكنك استخدام
Modifier.graphicsLayer{ }، لأنّ هذا المُعدِّل يتم تشغيله دائمًا في مرحلة
الرسم. لمزيد من المعلومات حول هذا الموضوع، يُرجى الاطّلاع على قسم تأجيل عمليات القراءة في مستندات الأداء.
تغيير توقيت الصور المتحركة
يستخدم Compose تلقائيًا حركات الارتداد لمعظم الحركات. تمنحك الصور المتحركة المستنِدة إلى الفيزياء تجربة أكثر واقعية. ويمكن أيضًا مقاطعتها لأنّها تأخذ في الاعتبار السرعة الحالية للعنصر بدلاً من الوقت الثابت.
إذا أردت إلغاء الإعدادات التلقائية، يمكنك ضبط animationSpec في جميع واجهات برمجة التطبيقات الخاصة بالحركة التي تم عرضها سابقًا لتخصيص طريقة تشغيل الحركة، سواء أردت تنفيذها خلال مدة معيّنة أو جعلها أكثر ارتدادًا.
في ما يلي ملخّص لخيارات animationSpec المختلفة:
-
spring: صور متحركة مستنِدة إلى الفيزياء، وهي الإعداد التلقائي لجميع الصور المتحركة. يمكنك تغيير الصلابة أو نسبة التخميد للحصول على مظهر وأسلوب مختلفَين للحركة. tween(اختصار لـ between): صور متحركة مستندة إلى المدة، يتم تحريكها بين قيمتَين باستخدام دالةEasing.-
keyframes: مواصفات لتحديد القيم في نقاط رئيسية معيّنة في الصور المتحركة repeatable: مواصفات مستندة إلى المدة يتم تشغيلها لعدد معيّن من المرات، يتم تحديده من خلالRepeatMode.infiniteRepeatable: مواصفات مستندة إلى المدة يتم تشغيلها إلى الأبد.-
snap: يتم الانتقال فورًا إلى القيمة النهائية بدون أي حركة.
يمكنك قراءة المستندات الكاملة لمزيد من المعلومات حول animationSpecs.
مراجع إضافية
للاطّلاع على المزيد من الأمثلة على الصور المتحركة الممتعة في Compose، يمكنك الاطّلاع على ما يلي:
- 5 صور متحركة سريعة في Compose
- تحريك قنديل البحر في Compose
- تخصيص
AnimatedContentفي Compose - التعرّف على دوال التخفيف في Compose