与单独处理动画相比,当我们处理触摸事件和动画时,必须考虑几个事项。首先,当触摸事件开始时,我们可能需要中断正在播放的动画,因为用户互动应当具有最高优先级。
在下面的示例中,我们使用 Animatable
表示圆形组件的偏移位置。触摸事件由 [pointerInput
] 修饰符处理。当检测到新的点按事件时,我们将调用 animateTo
以将偏移值通过动画过渡到点按位置。在动画播放期间也可能发生点按事件。在这种情况下,animateTo
会中断正在播放的动画,启动动画以过渡到新的目标位置,同时保持被中断的动画的速度。
package com.lujianfei.composeui.page.animation
import MyTopAppBar
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.VectorConverter
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
/**
* Author: lujianfei
* Date: 2024/3/27 11:25
* Description:
*/
@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun AnimationGesturePage(navController: NavHostController?= null) {
val offset = remember { Animatable(Offset(0f, 0f), Offset.VectorConverter) }
Scaffold(
topBar = {
MyTopAppBar(
title = {
Text("高级动画示例:手势")
},
navigationIcon = {
IconButton(onClick = { navController?.popBackStack() }) {
Icon(Icons.Filled.ArrowBack,"")
}
}
)
},
) { padding ->
Box(
modifier = Modifier.padding(padding)
.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)
}
}
}
}
}
) {
Box(modifier = Modifier.offset { offset.value.toIntOffset() }.size(50.dp).background(Color.Black))
}
}
}
private fun Offset.toIntOffset() = IntOffset(x.roundToInt(), y.roundToInt())