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

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

تخصيص الصور المتحركة باستخدام المَعلمة 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 هو اختصار للكلمة between (بين)، لأنّه يُستخدَم بين قيمتَين.

يمكنك أيضًا تحديد 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 المضمّنة: