إضافة تأثيرات متحركة لقيمة واحدة باستخدام animate*AsState
دوال animate*AsState
هي أبسط واجهات برمجة التطبيقات للرسوم المتحركة في أداة "الإنشاء" لتحريك قيمة واحدة. ما عليك سوى تقديم القيمة المستهدَفة (أو القيمة النهائية)، وتبدأ واجهة برمجة التطبيقات عرض الصورة المتحركة من القيمة الحالية إلى القيمة المحدّدة.
في ما يلي مثال على استخدام نسخة متحركة من ألفا باستخدام واجهة برمجة التطبيقات هذه. من خلال تضمين
القيمة المستهدَفة في animateFloatAsState
، تصبح قيمة ألفا الآن قيمة متحركة
بين القيم المقدَّمة (1f
أو 0.5f
في هذه الحالة).
var enabled by remember { mutableStateOf(true) } val alpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f, label = "alpha") Box( Modifier .fillMaxSize() .graphicsLayer(alpha = alpha) .background(Color.Red) )
لاحظ أنك لست بحاجة إلى إنشاء مثيل لأي فئة رسوم متحركة، أو التعامل مع المقاطعات. في الخلفية، سيتم إنشاء عنصر صورة متحركة (أي مثيل Animatable
) وتذكره في موقع الاستدعاء، مع استخدام قيمة الوجهة
الأولى كقيمة أولية. من الآن فصاعدًا، في أيّ وقت تقدّم فيه عنصرًا قابلاً للتجميع يتضمن قيمة مستهدفة مختلفة، سيتمّ بدء حركة متحرّكة تلقائيًا نحو تلك القيمة. إذا كان هناك صورة متحركة قيد التنفيذ، تبدأ الصورة المتحركة من
قيمتها الحالية (وسرعتها) وتتحرك نحو القيمة المستهدَفة. أثناء إنشاء الصور المتحركة، تتم إعادة إنشاء الصورة المتحركة هذه وتعرض قيمة محدثة للصور المتحركة في كل إطار.
توفّر ميزة "الإنشاء" تلقائيًا دوال animate*AsState
لأنواع البيانات Float
،
Color
، Dp
، Size
، Offset
، Rect
، Int
، IntOffset
،
IntSize
. يمكنك بسهولة إضافة إمكانية استخدام أنواع بيانات أخرى من خلال توفير TwoWayConverter
إلى animateValueAsState
يقبل نوعًا عامًا.
يمكنك تخصيص مواصفات الحركة من خلال تقديم AnimationSpec
.
يمكنك الاطّلاع على AnimationSpec للحصول على مزيد من المعلومات.
إضافة مؤثرات متحركة إلى عناصر متعدّدة في الوقت نفسه باستخدام انتقال
يدير Transition
صورة متحركة واحدة أو أكثر كعناصر ثانوية له ويشغّلها
بالتزامن بين حالات متعدّدة.
يمكن أن تكون الحالات من أي نوع بيانات. في كثير من الحالات، يمكنك استخدام نوع enum
مخصّص لضمان أمان النوع، كما في هذا المثال:
enum class BoxState { Collapsed, Expanded }
ينشئ updateTransition
مثيلًا من Transition
ويحفظه ويُعدِّل
حالته.
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "box state")
يمكنك بعد ذلك استخدام إحدى دوالّ التوسيع animate*
لتحديد ملف
متحركة فرعي في هذا الانتقال. حدِّد القيم المستهدَفة لكل حالة.
تعرض دوال animate*
هذه قيمة متحركة يتم تعديلها في كل إطار أثناء تشغيل الصورة المتحركة عند تعديل حالة الانتقال باستخدام updateTransition
.
val rect by transition.animateRect(label = "rectangle") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "border width") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
يمكنك اختياريًا تمرير مَعلمة transitionSpec
لتحديد قيمة AnimationSpec
مختلفة لكلّ مجموعة من مجموعات تغييرات حالة الانتقال. يمكنك الاطّلاع على AnimationSpec للحصول على مزيد من المعلومات.
val color by transition.animateColor( transitionSpec = { when { BoxState.Expanded isTransitioningTo BoxState.Collapsed -> spring(stiffness = 50f) else -> tween(durationMillis = 500) } }, label = "color" ) { state -> when (state) { BoxState.Collapsed -> MaterialTheme.colorScheme.primary BoxState.Expanded -> MaterialTheme.colorScheme.background } }
بعد وصول عملية النقل إلى الحالة المستهدَفة، ستكون قيمة Transition.currentState
نفسها في Transition.targetState
. ويمكن استخدام ذلك كإشارة لتحديد
ما إذا كانت عملية النقل قد اكتملت.
نريد أحيانًا الحصول على حالة أولية مختلفة عن الحالة المستهدفة الأولى. يمكننا استخدام updateTransition
مع MutableTransitionState
لتحقيق
هذا. على سبيل المثال، يسمح لنا ببدء عرض الصور المتحركة فور دخول الرمز إلى
المزيج.
// Start in collapsed state and immediately animate to expanded var currentState = remember { MutableTransitionState(BoxState.Collapsed) } currentState.targetState = BoxState.Expanded val transition = rememberTransition(currentState, label = "box state") // ……
لعملية انتقال أكثر تعقيدًا تتضمّن عدة دوال قابلة للتجميع، يمكنك
استخدام createChildTransition
لإنشاء عملية انتقال فرعية. يُعدّ هذا الأسلوب مفيدًا لفصل المخاوف
بين مكوّنات فرعية متعددة في عنصر مكوّن معقّد. سيكون للانتقال الرئيسي
عيّن على جميع قيم الصور المتحركة في الانتقالات الفرعية.
enum class DialerState { DialerMinimized, NumberPad } @Composable fun DialerButton(isVisibleTransition: Transition<Boolean>) { // `isVisibleTransition` spares the need for the content to know // about other DialerStates. Instead, the content can focus on // animating the state change between visible and not visible. } @Composable fun NumberPad(isVisibleTransition: Transition<Boolean>) { // `isVisibleTransition` spares the need for the content to know // about other DialerStates. Instead, the content can focus on // animating the state change between visible and not visible. } @Composable fun Dialer(dialerState: DialerState) { val transition = updateTransition(dialerState, label = "dialer state") Box { // Creates separate child transitions of Boolean type for NumberPad // and DialerButton for any content animation between visible and // not visible NumberPad( transition.createChildTransition { it == DialerState.NumberPad } ) DialerButton( transition.createChildTransition { it == DialerState.DialerMinimized } ) } }
استخدام الانتقال مع AnimatedVisibility
وAnimatedContent
تتوفّر AnimatedVisibility
وAnimatedContent
كدالتَي إضافة لدالة Transition
. يتم اشتقاق targetState
لملفَّي Transition.AnimatedVisibility
وTransition.AnimatedContent
من Transition
، وبدء عمليات النقل من/إلى الشاشة حسب الحاجة عند تغيُّر targetState
لملفَّي
Transition
. تتيح دوالّ الإضافات هذه نقل كلّ
الرسوم المتحركة للدخول/الخروج/تغيير الحجم التي كانت ستتمّ رفعها إلى Transition
في حال عدم استخدامها في
AnimatedVisibility
/AnimatedContent
.
باستخدام وظائف الإضافات هذه، يمكن رصد
تغيير حالة AnimatedVisibility
/AnimatedContent
من الخارج. بدلاً من المَعلمة المنطقية visible
،
يأخذ هذا الإصدار من AnimatedVisibility
دالة لامبدا تحوّل الحالة المستهدَفة
لانتقال العنصر الرئيسي إلى قيمة منطقية.
اطّلِع على AnimatedVisibility وAnimatedContent للاطّلاع على التفاصيل.
var selected by remember { mutableStateOf(false) } // Animates changes when `selected` is changed. val transition = updateTransition(selected, label = "selected state") val borderColor by transition.animateColor(label = "border color") { isSelected -> if (isSelected) Color.Magenta else Color.White } val elevation by transition.animateDp(label = "elevation") { isSelected -> if (isSelected) 10.dp else 2.dp } Surface( onClick = { selected = !selected }, shape = RoundedCornerShape(8.dp), border = BorderStroke(2.dp, borderColor), shadowElevation = elevation ) { Column( modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Text(text = "Hello, world!") // AnimatedVisibility as a part of the transition. transition.AnimatedVisibility( visible = { targetSelected -> targetSelected }, enter = expandVertically(), exit = shrinkVertically() ) { Text(text = "It is fine today.") } // AnimatedContent as a part of the transition. transition.AnimatedContent { targetState -> if (targetState) { Text(text = "Selected") } else { Icon(imageVector = Icons.Default.Phone, contentDescription = "Phone") } } } }
تجميع انتقال وجعله قابلاً لإعادة الاستخدام
بالنسبة إلى حالات الاستخدام البسيطة، يُعدّ تحديد الصور المتحركة للانتقالات في العنصر القابل للتجميع نفسه لواجهة المستخدم خيارًا صالحًا تمامًا. ومع ذلك، عند العمل على مكوّن معقّد يتضمّن عددًا من القيم المتحرّكة، قد تحتاج إلى فصل تنفيذ الحركة من واجهة المستخدم القابلة للتجميع.
ويمكنك إجراء ذلك عن طريق إنشاء فئة تحتوي على جميع قيم الصور المتحركة ودالة "update" تعرِض مثيلًا من تلك الفئة. ويمكن استخراج تنفيذ النقل إلى الدالة المنفصلة الجديدة. هذا النمط مفيد عندما تكون هناك حاجة إلى تمركز منطق الرسوم المتحركة، أو جعل الرسوم المتحركة المعقدة قابلة لإعادة الاستخدام.
enum class BoxState { Collapsed, Expanded } @Composable fun AnimatingBox(boxState: BoxState) { val transitionData = updateTransitionData(boxState) // UI tree Box( modifier = Modifier .background(transitionData.color) .size(transitionData.size) ) } // Holds the animation values. private class TransitionData( color: State<Color>, size: State<Dp> ) { val color by color val size by size } // Create a Transition and return its animation values. @Composable private fun updateTransitionData(boxState: BoxState): TransitionData { val transition = updateTransition(boxState, label = "box state") val color = transition.animateColor(label = "color") { state -> when (state) { BoxState.Collapsed -> Color.Gray BoxState.Expanded -> Color.Red } } val size = transition.animateDp(label = "size") { state -> when (state) { BoxState.Collapsed -> 64.dp BoxState.Expanded -> 128.dp } } return remember(transition) { TransitionData(color, size) } }
إنشاء صورة متحركة متكرّرة إلى ما لا نهاية باستخدام rememberInfiniteTransition
يحتوي InfiniteTransition
على صورة متحركة فرعية واحدة أو أكثر مثل Transition
، ولكن
تبدأ الصور المتحركة في التشغيل فور دخولها إلى التركيب ولا تتعطل
إلا إذا تمت إزالتها. يمكنك إنشاء مثيل من InfiniteTransition
باستخدام rememberInfiniteTransition
. يمكن إضافة صور متحركة للأطفال باستخدام
animateColor
أو animatedFloat
أو animatedValue
. وعليك أيضًا تحديد العنصر
initeRepeatable لتحديد مواصفات الصور المتحركة.
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Red, targetValue = Color.Green, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Box( Modifier .fillMaxSize() .background(color) )
واجهات برمجة التطبيقات المخصّصة للصور المتحركة ذات المستوى المنخفض
تم إنشاء جميع واجهات برمجة التطبيقات ذات المستوى العالي للرسوم المتحركة المذكورة في القسم السابق على أساس واجهات برمجة التطبيقات ذات المستوى المنخفض للرسوم المتحركة.
دوال animate*AsState
هي أبسط واجهات برمجة التطبيقات، وهي تعرِض تغييرًا فوريًا في
القيمة كقيمة للحركة. وتستند هذه الدالة إلى Animatable
، وهي واجهة برمجة تطبيقات مستندة إلى دالّة تناوب المهام لتحريك قيمة واحدة. ينشئ updateTransition
كائن انتقال يمكنه إدارة قيم متحركة متعدّدة وتنفيذها استنادًا
إلى تغيير الحالة. يشبه rememberInfiniteTransition
، ولكنه ينشئ انتقالًا
لانهائيًا يمكنه إدارة حركات متعدّدة تستمر في التشغيل
إلى أجل غير مسمى. جميع واجهات برمجة التطبيقات هذه قابلة للإنشاء باستثناء Animatable
، ما يعني أنّه يمكن إنشاء هذه الصور المتحركة خارج إطار الإنشاء.
تستند كل واجهات برمجة التطبيقات هذه إلى واجهة برمجة تطبيقات Animation
الأكثر أهمية. على الرغم من أنّ معظم التطبيقات لن تتفاعل مباشرةً مع Animation
، تتوفّر بعض إمكانات تخصيص Animation
من خلال واجهات برمجة تطبيقات ذات مستوى أعلى. اطّلِع على مقالة تخصيص الصور المتحركة للحصول على مزيد من المعلومات عن AnimationVector
وAnimationSpec
.
Animatable
: صورة متحرّكة لقيمة فردية تستند إلى كوروتين
Animatable
هو عنصر لتثبيت القيمة يمكنه إضافة تأثيرات متحركة للقيمة عند تغييرها من خلال
animateTo
. هذه هي واجهة برمجة التطبيقات التي تدعم تنفيذ animate*AsState
.
ويضمن ذلك الاستمرارية المتسقة والحصرية المتبادلة، ما يعني أنّ
تغيير القيمة يكون مستمرًا دائمًا وسيتم إلغاء أيّ حركة متحركة جارية.
يتم توفير العديد من ميزات Animatable
، بما في ذلك animateTo
، كدوﻻء معلّقة. وهذا يعني أنّه يجب لفّها في نطاق ملف برمجي قيد التنفيذ بشكل متزامن مناسب. على سبيل المثال، يمكنك استخدام العنصر القابل للتجميع LaunchedEffect
لإنشاء
نطاق فقط لمدة قيمة المفتاح المحدّدة.
// Start out gray and animate to green/red based on `ok` val color = remember { Animatable(Color.Gray) } LaunchedEffect(ok) { color.animateTo(if (ok) Color.Green else Color.Red) } Box( Modifier .fillMaxSize() .background(color.value) )
في المثال أعلاه، ننشئ مثيلًا من Animatable
ونتذكره
بالقيمة الأولية Color.Gray
. استنادًا إلى قيمة العلامة المنطقية
ok
، يتم عرض اللون بشكل متحرك إما على Color.Green
أو Color.Red
. يؤدي أي تغيير لاحق على القيمة المنطقية إلى بدء الرسم المتحرك إلى اللون الآخر. إذا كانت هناك رسم متحرك مستمر عند تغيير القيمة، فسيتم إلغاء الرسوم المتحركة، وتبدأ الرسوم المتحركة
الجديدة من قيمة اللقطة الحالية بالسرعة الحالية.
هذا هو تطبيق الصور المتحركة الذي يدعم animate*AsState
API
المذكور في القسم السابق. مقارنةً بـ animate*AsState
، يمنحنا استخدام
Animatable
مباشرةً إمكانية التحكّم بشكل أدق في عدة جوانب. أولاً،
يمكن أن يكون لـAnimatable
قيمة أولية مختلفة عن قيمته المستهدَفة الأولى.
على سبيل المثال، يعرض مثال الرمز أعلاه مربعًا رماديًا في البداية، يبدأ فورًا في التحوّل إلى اللون الأخضر أو الأحمر. ثانيًا، توفّر Animatable
المزيد من
العمليات على قيمة المحتوى، وهي snapTo
وanimateDecay
. snapTo
يضبط القيمة الحالية على القيمة المستهدَفة على الفور. يكون ذلك مفيدًا عندما لا يكون المؤثر المتحرك هو المصدر الوحيد للحقيقة ويجب مزامنته مع حالات
أخرى، مثل أحداث اللمس. يبدأ animateDecay
رسمًا متحركًا يبطئ
السرعة المحددة. ويكون هذا مفيدًا لتنفيذ سلوك الانتقال. اطّلِع على
الإيماءات والرسوم المتحركة للحصول على مزيد من المعلومات.
يتيح Animatable
تلقائيًا استخدام Float
وColor
، ولكن يمكن استخدام أي نوع بيانات
من خلال توفير TwoWayConverter
. اطّلِع على
AnimationVector للحصول على مزيد من المعلومات.
يمكنك تخصيص مواصفات الحركة من خلال تقديم AnimationSpec
.
يمكنك الاطّلاع على AnimationSpec للحصول على مزيد من المعلومات.
Animation
: صورة متحركة يتم التحكّم فيها يدويًا
Animation
هو أدنى مستوى لواجهة برمجة التطبيقات Animation API متاح. إنّ العديد من الصور المتحركة
التي رأيناها حتى الآن تستند إلى ميزة "الصور المتحركة". هناك نوعان فرعيان من Animation
:
TargetBasedAnimation
وDecayAnimation
.
يجب عدم استخدام Animation
إلا للتحكّم يدويًا في وقت عرض الصورة المتحركة.
لا تشمل السمة Animation
أي مفهوم لمراحل نشاطها. ويُعدّ
محرّكًا لحساب الرسوم المتحركة تستخدمه واجهات برمجة التطبيقات ذات المستوى الأعلى.
TargetBasedAnimation
تتناول واجهات برمجة التطبيقات الأخرى معظم حالات الاستخدام، ولكن باستخدام TargetBasedAnimation
مباشرةً،
يمكنك التحكّم في وقت تشغيل الصور المتحركة بنفسك. في المثال أدناه، يتم التحكّم يدويًا في وقت تشغيل TargetAnimation
استنادًا إلى وقت
الإطار الذي يوفّره withFrameNanos
.
val anim = remember { TargetBasedAnimation( animationSpec = tween(200), typeConverter = Float.VectorConverter, initialValue = 200f, targetValue = 1000f ) } var playTime by remember { mutableLongStateOf(0L) } LaunchedEffect(anim) { val startTime = withFrameNanos { it } do { playTime = withFrameNanos { it } - startTime val animationValue = anim.getValueFromNanos(playTime) } while (someCustomCondition()) }
DecayAnimation
على عكس TargetBasedAnimation
،
DecayAnimation
لا تتطلّب سمة targetValue
. بدلاً من ذلك، تحتسب
targetValue
استنادًا إلى شروط البدء، التي تم ضبطها من خلال initialVelocity
وinitialValue
وDecayAnimationSpec
المقدّمة.
غالبًا ما يتم استخدام الرسوم المتحرّكة للانحسار بعد إيماءة التمرير السريع لإبطاء العناصر إلى أن تتوقف. تبدأ سرعة الصورة المتحركة بالقيمة التي تم ضبطها باستخدام initialVelocityVector
وتتباطأ بمرور الوقت.
ما من اقتراحات في الوقت الحالي.
يُرجى محاولة تسجيل الدخول إلى حسابك على Google.