มีหลายสิ่งที่เราต้องพิจารณาเมื่อทำงานกับการโต้ตอบแบบสัมผัสและภาพเคลื่อนไหว เมื่อเทียบกับการทำงานกับภาพเคลื่อนไหวเพียงอย่างเดียว ก่อนอื่น เราอาจต้องขัดจังหวะภาพเคลื่อนไหวที่กำลังทำงานอยู่เมื่อการโต้ตอบแบบสัมผัสเริ่มต้นขึ้น เนื่องจากการโต้ตอบของผู้ใช้ควรมีลำดับความสำคัญสูงสุด
ในตัวอย่างต่อไปนี้ เราใช้ 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 (แทนที่จะใช้ Composable SwipeToDismiss) ออฟเซ็ตแนวนอนขององค์ประกอบจะแสดงเป็น Animatable API นี้มีลักษณะเฉพาะที่เป็นประโยชน์
ในภาพเคลื่อนไหวของท่าทางสัมผัส ค่าของตัวแปรนี้สามารถเปลี่ยนแปลงได้โดยการโต้ตอบแบบสัมผัสและภาพเคลื่อนไหว เมื่อได้รับเหตุการณ์การแตะลง เราจะหยุด 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 ปิดอยู่
- ภาพเคลื่อนไหวตามมูลค่า
- ลาก ปัด และเหวี่ยง
- ทำความเข้าใจท่าทางสัมผัส