يحتوي إنشاء النص على العديد من آليات الرسوم المتحركة المدمجة وقد يكون أمرًا مربكًا معرفة أيهما تختار. في ما يلي قائمة بحالات الاستخدام الشائعة للصور المتحركة. للحصول على معلومات أكثر تفصيلاً حول المجموعة الكاملة لخيارات واجهة برمجة التطبيقات المختلفة المتاحة لك، يُرجى الاطّلاع على مستندات إنشاء الصور المتحركة الكاملة.
تحريك السمات المشتركة القابلة للإنشاء
توفّر أداة 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
ضبط سلوك
المحتوى القابل للإنشاء عند ظهوره واختفائه. قراءة الكامل
Google لمزيد من المعلومات.
هناك خيار آخر لتحريك مستوى رؤية عنصر قابل للإنشاء، وهو تحريك
مع مرور الوقت باستخدام 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) ) }
إضافة تأثيرات متحركة إلى الحشو في عنصر قابل للتجميع
لتحريك المساحة المتروكة في عنصر قابل للإنشاء، استخدِم السمة 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-compos، لتحديد 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
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 = tween(100)) }
إنشاء صور متحركة متزامنة
استخدام واجهات برمجة تطبيقات الكوروتين (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
لاستخدام الحالة نفسها لتشغيل
العديد من الصور المتحركة المختلفة للمواقع في الوقت نفسه. يعرض المثال أدناه تأثيرًا متحركًا
لخاصيتين يتحكّم فيهما تغيير الحالة، وهما 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: التكوين والتخطيط والرسم. إذا كان المؤثر المرئي يغيّر مرحلة التنسيق، يجب إعادة تنسيق وإعادة رسم جميع العناصر المُركّبة المتأثرة. إذا كانت الرسوم المتحركة تظهر في مرحلة الرسم، فهي يكون الأداء تلقائيًا أكثر فعالية ممّا لو كنت تشغّل الرسوم المتحركة في حيث سيكون للقيام بعمل أقل بشكل عام.
لضمان أن يؤدي تطبيقك إلى أقل قدر ممكن من الوقت أثناء إنشاء الصور المتحركة، اختَر lambda
من Modifier
متى أمكن ذلك. يؤدي ذلك إلى تخطّي إعادة التركيب وتنفيذ
الصورة المتحركة خارج مرحلة التركيب، وإلا استخدِم
Modifier.graphicsLayer{ }
، لأنّ هذا المُعدِّل يتم تشغيله دائمًا في مرحلة
الرسم. لمزيد من المعلومات عن هذا الأمر، راجِع قسم قراءات التأجيل في
وثائق الأداء.
تغيير توقيت الرسوم المتحركة
تستخدم ميزة "إنشاء" الصور المتحركة الربيعي تلقائيًا في معظم الصور المتحركة. ينابيع
الرسوم المتحركة القائمة على الفيزياء، تبدو طبيعية أكثر. كما أنها يمكن أن تقاطعها
يأخذ في الاعتبار السرعة الحالية للجسم، بدلاً من الوقت الثابت.
إذا كنت تريد إلغاء الإعداد التلقائي، فستجد أن جميع واجهات برمجة تطبيقات الرسوم المتحركة الموضحة أعلاه
لديه القدرة على ضبط animationSpec
لتخصيص طريقة تشغيل الصور المتحركة،
ما إذا كنت تريد تنفيذه خلال مدة معينة أو أن يكون أكثر مرونة.
في ما يلي ملخّص لخيارات "animationSpec
" المختلفة:
spring
: صورة متحركة مستندة إلى الفيزياء، وهي الخيار التلقائي لكل الصور المتحركة. إِنْتَ تغيير الصلابة أو نسبة التخميد لإنشاء رسم متحرك مختلف الشكل والأسلوب.tween
(اختصار يعبِّر عن بين): صور متحركة مستندة إلى المدة بين قيمتين باستخدام دالةEasing
.keyframes
: مواصفات لتحديد قيم عند نقاط رئيسية معينة في الرسوم المتحركة.-
repeatable
: مواصفات مستندة إلى المدة يتم تنفيذها عددًا معيّنًا من المرات، يتم تحديدها من خلالRepeatMode
. infiniteRepeatable
: مواصفات مستندة إلى المدة تعمل بشكل دائم.snap
: الانتقال فورًا إلى القيمة النهائية بدون أي مؤثر حركي
اقرأ المستندات الكاملة لمزيد من المعلومات عن animationSpecs.
مصادر إضافية
لمزيد من الأمثلة على الصور المتحركة الممتعة في ميزة "الإنشاء"، اطّلِع على ما يلي:
- 5 صور متحرّكة سريعة في ميزة "إنشاء"
- تحريك Jellyfish في ميزة "الإنشاء"
- تخصيص
AnimatedContent
في Compose - إرخاء دوال التخفيف في Compose