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

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

تخصيص الصور المتحركة باستخدام المَعلمة 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),
    label = "alpha"
)

هناك أنواع مختلفة من 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
    ),
    label = "spring spec"
)

يمكن أن يعالج 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
    ),
    label = "tween delay"
)

يمكنك الاطّلاع على التخفيف للحصول على مزيد من المعلومات.

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

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

تحديد القيم عند 0 ملي ثانية ووقت المدة اختياري. إذا لم تحدِّد هذه القيم، سيتم ضبطها تلقائيًا على قيم بدء وانتهاء الحركة المتحرّكة، على التوالي.

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

تكرار صورة متحركة باستخدام 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
    ),
    label = "repeatable spec"
)

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

تكون infiniteRepeatable مشابهة لـ repeatable، ولكنّها تتكرّر لعددٍ لانهائي من التكرارات.

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

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

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

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

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

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

تستخدم عمليات 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
        ),
        label = "custom easing"
    )
    // ……
}

توفّر ميزة "الإنشاء" العديد من دوال Easing المضمّنة التي تغطي معظم حالات الاستخدام. اطّلِع على السرعة - التصميم المادّي للحصول على مزيد من المعلومات حول كيفية استخدام ميزة "التخفيف" حسب السيناريو.

إضافة مؤثرات متحركة إلى أنواع البيانات المخصّصة من خلال التحويل إلى AnimationVector والعكس

تتوافق معظم واجهات برمجة التطبيقات لإنشاء الصور المتحركة مع Float وColor وDp وأنواع بيانات dasar أخرى كقيم للصور المتحركة تلقائيًا، ولكنك تحتاج أحيانًا إلى إضافة تأثيرات متحركة لأنواع بيانات أخرى، بما في ذلك الأنواع المخصّصة. أثناء عرض الصورة المتحركة، يتم تمثيل أي قيمة متحركة برمز 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)
            }
        ),
        label = "size"
    )
}

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