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

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

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

إنشاء صور متحركة بين الإطارات الرئيسية بسلاسة باستخدام keyframesWithSplines

لإنشاء صورة متحرّكة تتبع منحنى سلسًا أثناء الانتقال بين القيم، يمكنك استخدام keyframesWithSplines بدلاً من مواصفات الصورة المتحرّكة keyframes.

val offset by animateOffsetAsState(
    targetValue = Offset(300f, 300f),
    animationSpec = keyframesWithSpline {
        durationMillis = 6000
        Offset(0f, 0f) at 0
        Offset(150f, 200f) atFraction 0.5f
        Offset(0f, 100f) atFraction 0.7f
    }
)

تكون إطارات المفاتيح المستندة إلى خطوط الانحناء مفيدة بشكل خاص للحركة الثنائية الأبعاد للعناصر على الشاشة.

تعرض الفيديوهات التالية الاختلافات بين keyframes وkeyframesWithSpline مع توفّر مجموعة الإحداثيات x وy نفسها التي يجب أن تتبعها الدائرة.

keyframes keyframesWithSplines

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

تكرار صورة متحركة باستخدام 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"
    )
    // ……
}

توفّر Compose العديد من دوال Easing المضمّنة التي تغطي معظم حالات الاستخدام. يمكنك الاطّلاع على السرعة - التصميم المتعدد الأبعاد للحصول على مزيد من المعلومات حول نوع تغيير السرعة الذي يجب استخدامه حسب السيناريو.

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearEasing
  • LinearEasing
  • CubicBezierEasing
  • عرض المزيد

تحريك أنواع البيانات المخصّصة من خلال التحويل إلى 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)
            }
        ),
        label = "size"
    )
}

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