تتضمّن أداة الإنشاء العديد من آليات الرسوم المتحركة المضمّنة، وقد يكون من الصعب معرفة الملاءمة لاختيار أحدها. في ما يلي قائمة بحالات استخدام الرسوم المتحركة الشائعة. للحصول على معلومات أكثر تفصيلاً حول المجموعة الكاملة من الخيارات المختلفة لواجهة برمجة التطبيقات المتاحة لك، يمكنك الاطّلاع على مستندات إنشاء الصور المتحركة الكاملة.
إضافة مؤثرات متحركة إلى الخصائص الشائعة القابلة للتجميع
توفّر أداة 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()
مقبولاً لإعداد لون لقطة واحدة، ولكن عند
تحريك لون مع مرور الوقت، يمكن أن يؤدي ذلك إلى حدوث إعادة ابتكار أكثر من
الضروري.
لإضافة تأثير متكرّر للخلفية، اطّلِع على قسم تكرار تأثير متحرّك.
إضافة تأثيرات متحركة إلى حجم عنصر قابل للتجميع

تتيح لك ميزة "إنشاء" إضافة تأثيرات حركية إلى حجم العناصر القابلة للإنشاء بعدة طرق مختلفة. استخدِم
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) ) }
لون النص المتحرّك

لتحريك لون النص، استخدِم 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 = { 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
.
إضافة تأثيرات متحركة أثناء الانتقال إلى وجهات مختلفة

لإضافة مؤثرات متحركة إلى عمليات النقل بين العناصر القابلة للتجميع عند استخدام العنصر
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
" لتحديد كيفية التنقّل ذهابًا وإيابًا.
استخدِم 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 }
بدء صورة متحركة عند تشغيل عنصر قابل للتجميع
يتم تشغيل LaunchedEffect
عند إدخال عنصر قابل للإنشاء في المقطوعة الموسيقية. يبدأ تشغيل رسم متحرك عند تشغيل عنصر قابل للإنشاء، ويمكنك استخدامه لتغيير حالة الرسوم المتحركة. ويؤدي استخدام 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)) }
إنشاء صور متحركة متزامنة

استخدِم واجهات برمجة تطبيقات coroutine (Animatable#animateTo()
أو animate
) أو
واجهة برمجة التطبيقات Transition
لإنشاء صور متحركة متزامنة. في حال استخدام عدة دالات
لبدء في سياق دالة coroutine، يتم تشغيل الرسوم المتحرّكة في
الوقت نفسه:
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 في حدوث مشاكل في الأداء. ويعود السبب في ذلك إلى طبيعة الرسوم المتحركة: نقل أو تغيير وحدات البكسل على الشاشة بسرعة، إطارًا تلو الآخر لخلق وهم الحركة.
ننصحك بالاطّلاع على المراحل المختلفة لإنشاء المحتوى: التصميم والتنسيق والرسم. إذا كان المؤثر المرئي يغيّر مرحلة التنسيق، يجب إعادة تنسيق وإعادة رسم جميع العناصر المُركّبة المتأثرة. إذا كان عرض الرسوم المتحركة يحدث في مرحلة الرسم، سيكون أداؤه بشكلٍ default أفضل مما لو كنت ستعرض الرسوم المتحركة في مرحلة تنسيق المحتوى، لأنّه سيكون عليه تنفيذ عمل أقل بشكل عام.
ننصحك باختيار إصدار lambda
من Modifier
كلما أمكن ذلك لضمان إجراء تطبيقك بأقل قدر ممكن من الرسومات أثناء إنشاء الصور المتحركة. يؤدي ذلك إلى تخطّي إعادة التركيب وتنفيذ
الصورة المتحركة خارج مرحلة التركيب، وإلا استخدِم
Modifier.graphicsLayer{ }
، لأنّ هذا المُعدِّل يتم تشغيله دائمًا في مرحلة
الرسم. لمزيد من المعلومات حول هذا الموضوع، اطّلِع على قسم تأجيل عمليات القراءة في
مستندات الأداء.
تغيير توقيت الرسوم المتحركة
يستخدم تطبيق "الإنشاء" تلقائيًا رسومًا متحركة نابضة لمعظم الرسوم المتحركة. تبدو العناصر المرنة أو
الرسوم المتحركة المستندة إلى الفيزياء أكثر طبيعية. ويمكن أيضًا إيقافها لأنّها تأخذ في الاعتبار السرعة الحالية للجسم بدلاً من وقت ثابت.
إذا كنت تريد إلغاء الإعداد التلقائي، تتيح لك جميع واجهات برمجة التطبيقات للرسوم المتحركة الموضّحة أعلاه
ضبط animationSpec
لتخصيص طريقة تشغيل الرسوم المتحركة،
سواء كنت تريد تنفيذها خلال مدة معيّنة أو أن تكون أكثر مرونة.
في ما يلي ملخّص لخيارات animationSpec
المختلفة:
spring
: صورة متحركة مستندة إلى الفيزياء، وهي الخيار التلقائي لكل الصور المتحركة. يمكنك تغيير مستوى الصلابة أو التخميد للحصول على مظهر وشكل مختلف للصور المتحركة.-
tween
(اختصار between): صورة متحركة مستندة إلى المدة، تُنشئ صورة متحركة بين قيمتَين باستخدام دالةEasing
. -
keyframes
: مواصفات لتحديد القيم في نقاط رئيسية معيّنة في الصورة المتحركة -
repeatable
: مواصفات مستندة إلى المدة يتم تنفيذها عددًا معيّنًا من المرات، يتم تحديدها من خلالRepeatMode
. -
infiniteRepeatable
: مواصفات مستندة إلى المدة يتم تشغيلها إلى الأبد. snap
: يتم الانتقال على الفور إلى القيمة النهائية بدون أيّ حركة.

اطّلِع على المستندات الكاملة للحصول على مزيد من المعلومات عن animationSpecs.
مصادر إضافية
للاطلاع على المزيد من الأمثلة على الصور المتحركة الممتعة في Compose، اطّلِع على ما يلي:
- 5 صور متحركة سريعة في Compose
- تحريك قنديل البحر في Compose
- تخصيص
AnimatedContent
في ميزة "إنشاء" - الانتقال إلى دوال Easing في ميزة "الإنشاء"