تخصيص الصور المتحركة

عادةً ما تقبل العديد من واجهات برمجة تطبيقات Animation المعلَمات لتخصيص سلوكها.

تخصيص الصور المتحركة باستخدام مَعلمة AnimationSpec

وتتيح معظم واجهات برمجة التطبيقات للصور المتحركة للمطوّرين تخصيص مواصفات الصور المتحركة من خلال مَعلمة AnimationSpec اختيارية.

val alpha: Float by animateFloatAsState(
    targetValue = if (enabled) 1f else 0.5f,
    // Configure the animation duration and easing.
    animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing)
)

هناك أنواع مختلفة من AnimationSpec لإنشاء أنواع مختلفة من الصور المتحركة.

إنشاء صورة متحركة تستنِد إلى قوانين الفيزياء باستخدام "spring"

ينشئ spring صورة متحركة تستند إلى الفيزياء بين قيم البداية والنهاية. ويتم استخدام مَعلمتَين: dampingRatio وstiffness.

تحدّد السمة dampingRatio مستوى النضج الذي يجب أن يكون نبعًا في الربيع. والقيمة التلقائية هي Spring.DampingRatioNoBouncy.

الشكل 1. ضبط نِسب مختلفة لتخميد الربيع.

تحدِّد السمة stiffness مدى سرعة تحرك الربيع نحو القيمة النهائية. والقيمة التلقائية هي Spring.StiffnessMedium.

الشكل 2. ضبط صلابة مختلفة للزنبرك

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioHighBouncy,
        stiffness = Spring.StiffnessMedium
    )
)

بإمكان spring معالجة الانقطاعات بسلاسة أكبر من الأنواع AnimationSpec المستندة إلى المدة، لأنّها تضمن استمرارية السرعة عندما تتغيّر القيمة المستهدفة وسط الصور المتحركة. يتم استخدام spring باعتباره AnimationSpec التلقائي من خلال العديد من واجهات برمجة تطبيقات الصور المتحركة، مثل animate*AsState وupdateTransition.

على سبيل المثال، في حال تطبيق إعدادات spring على الصورة المتحركة التالية التي تعمل باللمس، ستلاحظ أنّ استخدام tween لا يستجيب أثناء استخدام spring عند إيقاف الصورة المتحركة أثناء تقدمها.

الشكل 3. ضبط مواصفات tween مقابل spring للصور المتحركة وإيقافها

تحريك القيمة بين قيم البداية والنهاية باستخدام منحنى التخفيف باستخدام tween

يتحرك tween بين قيم البداية والنهاية على durationMillis المحددة باستخدام منحنى التخفيف. tween هو اختصار لكلمة بين - حيث إنه بين قيمتين.

يمكنك أيضًا تحديد delayMillis لتأجيل بداية الصورة المتحركة.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = tween(
        durationMillis = 300,
        delayMillis = 50,
        easing = LinearOutSlowInEasing
    )
)

راجع خفض الإرخاء للحصول على مزيد من المعلومات.

التحريك إلى قيم معيّنة في توقيتات معيّنة باستخدام keyframes

يتحرك keyframes بناءً على قيم اللقطة المحددة في طوابع زمنية مختلفة في مدة الصورة المتحركة. في أي وقت محدد، سيتم إدخال قيمة الرسوم المتحركة بين قيمتين للإطار الرئيسي. بالنسبة إلى كل من هذه الإطارات الرئيسية، يمكن تحديد تغيير السرعة لتحديد منحنى الاستيفاء.

ويكون اختياريًا تحديد القيم عند 0 ملي ثانية وفي المدة الزمنية. إذا لم تحدد هذه القيم، فسيتم تعيينها افتراضيًا على قيم البداية والنهاية للرسوم المتحركة، على التوالي.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = keyframes {
        durationMillis = 375
        0.0f at 0 with LinearOutSlowInEasing // for 0-15 ms
        0.2f at 15 with FastOutLinearInEasing // for 15-75 ms
        0.4f at 75 // ms
        0.4f at 225 // ms
    }
)

تكرار صورة متحركة مع "repeatable"

يشغّل repeatable رسمًا متحركًا يعتمد على مدة (مثل tween أو keyframes) بشكل متكرر حتى يصل إلى عدد التكرار المحدد. يمكنك ضبط المَعلمة repeatMode لتحديد ما إذا كان يجب تكرار الصورة المتحركة من خلال بدئها من البداية (RepeatMode.Restart) أو من النهاية (RepeatMode.Reverse).

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = repeatable(
        iterations = 3,
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    )
)

تكرار صورة متحركة بلا نهاية باستخدام "infiniteRepeatable"

تشبه الدالة infiniteRepeatable repeatable، لكنها تتكرر لعدد غير محدود من التكرارات.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    )
)

في الاختبارات التي تستخدم ComposeTestRule، لا يتم تشغيل الصور المتحركة التي تستخدم infiniteRepeatable. سيتم عرض المكون باستخدام القيمة الأولية لكل قيمة متحركة.

المحاذاة فورًا إلى القيمة النهائية باستخدام snap

snap هو عنصر AnimationSpec خاص يبدّل القيمة فورًا إلى قيمة النهاية. يمكنك تحديد delayMillis لتأخير بدء الصورة المتحركة.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = snap(delayMillis = 50)
)

ضبط دالة تخفيف مخصّصة

وتستخدم عمليات AnimationSpec المستندة إلى المدة (مثل tween أو keyframes) Easing لضبط جزء الرسم المتحرك. يسمح هذا للقيمة المتحركة بتسريع والبطء، بدلاً من التحرك بمعدل ثابت. الكسر هو قيمة بين 0 (بداية) و1.0 (نهاية) تشير إلى النقطة الحالية في الرسوم المتحركة.

التخفيف هو في الواقع دالة تأخذ قيمة كسر بين 0 و1.0 وترجع عددًا عشريًا. يمكن أن تكون القيمة المعروضة خارج الحد لتمثيل تجاوز أو عجز عن الهدف. يمكن إنشاء مستوى سرعة مخصص مثل الرمز أدناه.

val CustomEasing = Easing { fraction -> fraction * fraction }

@Composable
fun EasingUsage() {
    val value by animateFloatAsState(
        targetValue = 1f,
        animationSpec = tween(
            durationMillis = 300,
            easing = CustomEasing
        )
    )
    // ……
}

توفّر ميزة إنشاء الرسائل العديد من دوال Easing المدمَجة التي تغطي معظم حالات الاستخدام. يمكنك الاطّلاع على السرعة - التصميم المتعدد الأبعاد للحصول على مزيد من المعلومات حول إعدادات "السرعة" التي يمكن استخدامها وفقًا لحالتك المحدّدة.

تحريك أنواع البيانات المخصّصة من خلال التحويل من وإلى "AnimationVector"

تتوافق معظم واجهات برمجة تطبيقات Compose للصور المتحركة مع Float وColor وDp وغيرها من أنواع البيانات الأساسية كقيم للصور المتحركة تلقائيًا، ولكنّك تحتاج أحيانًا إلى تحريك أنواع بيانات أخرى، بما في ذلك الأنواع المخصَّصة. أثناء إنشاء الصور المتحركة، يتم تمثيل أي قيمة متحركة على أنها AnimationVector. يتم تحويل القيمة إلى AnimationVector والعكس صحيح باستخدام TwoWayConverter مقابل بحيث يمكن لنظام الصور المتحركة الأساسي التعامل معها بشكل موحد. على سبيل المثال، يتم تمثيل Int باعتباره AnimationVector1D الذي يحتوي على قيمة عائمة واحدة. يبدو حساب TwoWayConverter الخاص بـ Int على النحو التالي:

val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
    TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })

وColor هي في الأساس مجموعة من 4 قيم، وهي الأحمر والأخضر والأزرق وألفا، ولذلك يتم تحويل Color إلى AnimationVector4D تحتوي على 4 قيم عائمة. وبهذه الطريقة، يتم تحويل كل نوع بيانات مُستخدَم في الصور المتحركة إلى AnimationVector1D أو AnimationVector2D أو AnimationVector3D أو AnimationVector4D بناءً على أبعاده. ويسمح هذا بتحريك مكونات مختلفة من الكائن بشكل مستقل، لكل منها تتبع سرعته الخاصة. يمكن الوصول إلى أدوات التحويل المضمَّنة لأنواع البيانات الأساسية باستخدام أدوات تحويل مثل Color.VectorConverter أو Dp.VectorConverter.

عندما تريد إتاحة نوع بيانات جديد كقيمة متحركة، يمكنك إنشاء TwoWayConverter خاص بك وتقديمه إلى واجهة برمجة التطبيقات. على سبيل المثال، يمكنك استخدام animateValueAsState لتحريك نوع البيانات المخصّص كما يلي:

data class MySize(val width: Dp, val height: Dp)

@Composable
fun MyAnimation(targetSize: MySize) {
    val animSize: MySize by animateValueAsState(
        targetSize,
        TwoWayConverter(
            convertToVector = { size: MySize ->
                // Extract a float value from each of the `Dp` fields.
                AnimationVector2D(size.width.value, size.height.value)
            },
            convertFromVector = { vector: AnimationVector2D ->
                MySize(vector.v1.dp, vector.v2.dp)
            }
        )
    )
}

تتضمّن القائمة التالية بعض VectorConverter المدمجة: