由于速度为t=x/v,因此可以计算将哪个持续时间传递给补间,并将其作为
private fun calculateDuration(
targetValue: Offset,
velocity: Float
): Int {
val xPos = targetValue.x
val yPos = targetValue.y
val distance = sqrt(xPos * xPos + yPos + yPos)
return (distance / velocity * 1000).toInt()
}
以恒定的速度创造一种状态
@Composable
fun animateConstantSpeedOffsetAsState(
initialOffset: Offset = Offset.Zero,
targetValue: Offset,
velocity: Float,
label: String = "OffsetAnimation",
finishedListener: ((Offset) -> Unit)? = null
): State<Offset> {
require(velocity > 0f)
var previousOffset by remember {
mutableStateOf(initialOffset)
}
val durationMillis by remember {
mutableIntStateOf(calculateDuration(targetValue.minus(previousOffset), velocity))
}.apply {
val duration = calculateDuration(targetValue.minus(previousOffset), velocity)
if (duration > 0) {
this.intValue = duration
}
}
previousOffset = targetValue
val animationSpec = tween<Offset>(
durationMillis = durationMillis,
easing = LinearEasing
)
return animateValueAsState(
targetValue,
Offset.VectorConverter,
animationSpec,
label = label,
finishedListener = {
previousOffset = targetValue
finishedListener?.invoke(it)
}
)
}
红色圆圈使用AnimateAsSate,而绿色圆圈使用恒速
演示
@Preview
@Composable
private fun AnimationVelocityTest() {
var isClicked by remember {
mutableStateOf(false)
}
val target1 = if (isClicked.not()) Offset.Zero
else Offset(500f, 500f)
val target2 = if (isClicked.not()) Offset.Zero
else Offset(1000f, 1000f)
val offset1 by animateOffsetAsState(
targetValue = target1,
animationSpec = tween(4000, easing = LinearEasing),
label = ""
)
val offset2 by animateOffsetAsState(
targetValue = target2,
animationSpec = tween(4000, easing = LinearEasing),
label = ""
)
val offset3 by animateConstantSpeedOffsetAsState(
targetValue = target1,
velocity = 250f,
)
val offset4 by animateConstantSpeedOffsetAsState(
targetValue = target2,
velocity = 250f,
)
Canvas(
modifier = Modifier.fillMaxSize()
.padding(20.dp)
.clickable {
isClicked = isClicked.not()
}
) {
drawCircle(
color = Color.Red,
radius = 50f,
center = offset1
)
translate(top = 100f) {
drawCircle(
color = Color.Red,
radius = 50f,
style = Stroke(4.dp.toPx()),
center = offset2
)
}
translate(top = 200f) {
drawCircle(
color = Color.Green,
radius = 50f,
center = offset3
)
}
translate(top = 300f) {
drawCircle(
color = Color.Green,
radius = 50f,
style = Stroke(4.dp.toPx()),
center = offset4
)
}
}
}
Another 演示
@Preview
@Composable
private fun AnimationVelocityTest2() {
var isClicked by remember {
mutableStateOf(false)
}
var dynamicTarget by remember {
mutableStateOf(Offset.Zero)
}
LaunchedEffect(isClicked) {
if (isClicked) {
dynamicTarget = dynamicTarget.plus(Offset(100f, 100f))
}
}
val offset by animateConstantSpeedOffsetAsState(
targetValue = dynamicTarget,
velocity = 100f,
)
Canvas(
modifier = Modifier.fillMaxSize()
.padding(20.dp)
.clickable {
isClicked = isClicked.not()
}
) {
drawRect(
color = Color.Magenta,
size = Size(100f, 100f),
style = Stroke(4.dp.toPx()),
topLeft = offset
)
}
}