我试图在每次页面刷新时增加动画速度,但它没有按预期工作。下面是我的代码:
@Composable
fun ScreenReport(
) {
var isRefreshing by remember { mutableStateOf(false) }
val ptrState = rememberPullToRefreshState()
var animationSpeed by remember { mutableIntStateOf(1000) }
val infiniteTransition = rememberInfiniteTransition(label = "")
val angle by infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 360F,
animationSpec = infiniteRepeatable(
animation = tween(animationSpeed, easing = LinearEasing)
), label = ""
)
Box(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp, top = 16.dp)
.background(MaterialTheme.colorScheme.background)
) {
PullToRefreshBox(
modifier = Modifier.padding(8.dp),
state = ptrState,
isRefreshing = isRefreshing,
onRefresh = {
isRefreshing = true
coroutineScope.launch {
animationSpeed += 1000
isRefreshing = false
}
},
) {
Column(
modifier = Modifier
.align(Alignment.TopCenter)
.verticalScroll(scrollState)
) {
Log.i("TESTING", animationSpeed.toString())
MesIcon(
painterResource = R.drawable.ic_cyclone,
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.graphicsLayer {
rotationZ = angle
}
)
// More code here that's not relevant
}
我添加的
Log
显示动画速度每次都在更新,但图像上的旋转速度根本没有改变。
有人可以帮我理解为什么吗?
发生这种情况的原因是 TransitionAnimationState 创建一次,并且不会根据源代码中的任何其他参数更改。
@Composable
public fun <T, V : AnimationVector> InfiniteTransition.animateValue(
initialValue: T,
targetValue: T,
typeConverter: TwoWayConverter<T, V>,
animationSpec: InfiniteRepeatableSpec<T>,
label: String = "ValueAnimation"
): State<T> {
val transitionAnimation = remember {
TransitionAnimationState(initialValue, targetValue, typeConverter, animationSpec, label)
}
// Rest of the code
}
取而代之的是,您可以使用 Animatable,每次在循环中更改 animationSpec
中的动画持续时间以创建无限过渡时,您都可以使用它来制作不同的
LaunchedEffect
动画。您传递给补间的不是动画速度而是动画持续时间。如果你想让它旋转得更快,你需要减小该值。
但是对于Animatable来说重要的是
当动画被中断时,它会从当前值开始到目标并具有相同的缓动,这意味着如果从0度到360度需要1秒,而当你在300度中断时它会再次出现需要 1 秒才能达到 360,这使得中断后的动画速度变慢。因此,您可以根据 360 度的接近程度调整第一个动画持续时间
@Preview
@Composable
fun ScreenReport() {
var isRefreshing by remember { mutableStateOf(false) }
val ptrState = rememberPullToRefreshState()
var animationDuration by remember { mutableIntStateOf(2000) }
val animatable = remember {
Animatable(0f)
}
LaunchedEffect(animationDuration) {
while (isActive) {
try {
animatable.animateTo(targetValue = 360f, animationSpec = tween(animationDuration, easing = LinearEasing))
if (animatable.value >= 360f) {
animatable.snapTo(targetValue = 0f)
}
} catch (exception: CancellationException) {
val currentValue = animatable.value
// This makes sure that animation after interruption runs at same duration with newly assigned duration
animatable.animateTo(
targetValue = 360f,
tween((animationDuration * currentValue / 360).toInt(), easing = LinearEasing)
)
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp, top = 16.dp)
.background(MaterialTheme.colorScheme.background),
contentAlignment = Alignment.Center
) {
PullToRefreshBox(
modifier = Modifier
.fillMaxSize()
.padding(8.dp),
state = ptrState,
isRefreshing = isRefreshing,
onRefresh = {
isRefreshing = true
animationDuration += 2000
isRefreshing = false
},
) {
Column(
modifier = Modifier
.align(Alignment.TopCenter)
.verticalScroll(rememberScrollState())
) {
Icon(
imageVector = Icons.Filled.Star,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.size(100.dp)
.graphicsLayer {
rotationZ = animatable.value
},
contentDescription = null
)
Text(
"angle: ${animatable.value.toInt()}\n" +
"animationDuration: $animationDuration"
)
}
}
}
}