هناك عدة أمور يجب أخذها في الاعتبار عند العمل مع أحداث اللمس والرسومات المتحرّكة، مقارنةً بالعمل مع الرسومات المتحرّكة فقط. أولاً، قد نحتاج إلى إيقاف صورة متحركة جارية عند بدء أحداث اللمس لأنّ تفاعل المستخدم يجب أن يكون له الأولوية القصوى.
في المثال أدناه، نستخدم Animatable
لتمثيل موضع الإزاحة
لمكوّن الدائرة. تتم معالجة أحداث اللمس باستخدام المُعدِّل
pointerInput
. عند رصد حدث نقرة جديد، نُجري طلبًا إلى animateTo
لتحريك قيمة
الموضع النسبي إلى موضع النقرة. يمكن أن يحدث حدث النقر أثناء عرض الصورة المتحركة
أيضًا، وفي هذه الحالة، يوقف animateTo
الصورة المتحركة الجارية ويشغّل
الصورة المتحركة إلى الموضع المستهدَف الجديد مع الحفاظ على سرعة
الصورة المتحركة التي تم إيقافها.
@Composable fun Gesture() { val offset = remember { Animatable(Offset(0f, 0f), Offset.VectorConverter) } Box( modifier = Modifier .fillMaxSize() .pointerInput(Unit) { coroutineScope { while (true) { // Detect a tap event and obtain its position. awaitPointerEventScope { val position = awaitFirstDown().position launch { // Animate to the tap position. offset.animateTo(position) } } } } } ) { Circle(modifier = Modifier.offset { offset.value.toIntOffset() }) } } private fun Offset.toIntOffset() = IntOffset(x.roundToInt(), y.roundToInt())
هناك نمط شائع آخر هو أنّنا نحتاج إلى مزامنة قيم الرسوم المتحركة مع القيم
المنبثقة من أحداث اللمس، مثل السحب. في المثال أدناه، نرى أنّ الإجراء "تمرير سريع للتصغير" تم تنفيذه كعنصر Modifier
(بدلاً من استخدام العنصر القابل للتجميع
SwipeToDismiss
). يتم تمثيل الإزاحة الأفقية للعنصر على شكل
Animatable
. تتميز واجهة برمجة التطبيقات هذه بسمة مفيدة في الرسوم المتحرّكة للّمسات. ويمكن تغيير قيمته
بواسطة أحداث اللمس بالإضافة إلى الحركة. عندما نتلقّى حدث
touch down، نوقف Animatable
باستخدام طريقة stop
حتى يتم اعتراض أي
صورة متحركة جارية.
أثناء حدث السحب، نستخدم snapTo
لتعديل قيمة Animatable
باستخدام
القيمة المحسوبة من أحداث اللمس. بالنسبة إلى الرمي، يوفّر Compose
VelocityTracker
لتسجيل أحداث السحب واحتساب السرعة. يمكن
إرسال السرعة مباشرةً إلى animateDecay
لعرض الصور المتحركة للرمي. عندما نريد تحريك
قيمة العنصر المُحوَّل إلى موضع أصلي، نحدِّد قيمة العنصر المُحوَّل المستهدفة
0f
باستخدام الطريقة animateTo
.
fun Modifier.swipeToDismiss( onDismissed: () -> Unit ): Modifier = composed { val offsetX = remember { Animatable(0f) } pointerInput(Unit) { // Used to calculate fling decay. val decay = splineBasedDecay<Float>(this) // Use suspend functions for touch events and the Animatable. coroutineScope { while (true) { val velocityTracker = VelocityTracker() // Stop any ongoing animation. offsetX.stop() awaitPointerEventScope { // Detect a touch down event. val pointerId = awaitFirstDown().id horizontalDrag(pointerId) { change -> // Update the animation value with touch events. launch { offsetX.snapTo( offsetX.value + change.positionChange().x ) } velocityTracker.addPosition( change.uptimeMillis, change.position ) } } // No longer receiving touch events. Prepare the animation. val velocity = velocityTracker.calculateVelocity().x val targetOffsetX = decay.calculateTargetValue( offsetX.value, velocity ) // The animation stops when it reaches the bounds. offsetX.updateBounds( lowerBound = -size.width.toFloat(), upperBound = size.width.toFloat() ) launch { if (targetOffsetX.absoluteValue <= size.width) { // Not enough velocity; Slide back. offsetX.animateTo( targetValue = 0f, initialVelocity = velocity ) } else { // The element was swiped away. offsetX.animateDecay(velocity, decay) onDismissed() } } } } } .offset { IntOffset(offsetX.value.roundToInt(), 0) } }
أفلام مُقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما تكون لغة JavaScript غير مفعّلة.
- الصور المتحركة المستندة إلى القيمة
- السحب والتمرير السريع والقذف
- التعرّف على الإيماءات