Single-Shot/Fire-and-Forget 用于对基本状态变化进行动画处理的动画师(animate*AsState 的单次等效))

Single-Shot/Fire-and-Forget Animator for animating basic state changes (animate*AsState's single-shot equivalent))

我只想制作一次动画,并且正在尝试不同的选项。我想使用 animate*AsState 但这不起作用,因为你必须在进入该屏幕时通过使用 LaunchedEffect 执行某种 SideEffect 来触发它,如下所示:

@Composable
fun StartAnimation(width: Float = 0f) {
    var startAnimation by remember { mutableStateOf(false) }
    val widthAnimation = animateDpAsState(targetValue = if(startAnimation) width.dp else 0.dp, tween(600))
    Box(
        modifier = Modifier
            .padding(top = 30.dp)
            .padding(horizontal = 30.dp)
            .fillMaxWidth()
            .height(20.dp)
            .background(Color.Gray),
        contentAlignment = Alignment.CenterStart
    ) {
        Box(
            Modifier
                .background(Color.Black)
                .width(widthAnimation.value)
                .height(20.dp)
                .background(Color.Black)
        )
    }

    LaunchedEffect(key1 = true) {
        startAnimation = true
    }
}

也许有更好的方法来做到这一点,但我错过了?

我认为没有任何东西可以让您摆脱 LaunchedEffect。但这可能会更好,至少你不必使用那个人为变量:

val animatableWidth = remember { Animatable(0.dp, Dp.VectorConverter) }
LaunchedEffect(width) {
    animatableWidth.animateTo(width.dp, tween(600))
}

只需创建一个自定义 fire-and-forget 系统。作为 single-shot 动画师工作,几乎可以并行生成任意多次。

@Composable
fun animateFloatAsState(
    initialValue: Float,
    targetValue: Float,
    delay: Long = 0,
    animationSpec: AnimationSpec<Float> = spring<Float>(),
    visibilityThreshold: Float = 0.01f,
    finishedListener: ((Float) -> Unit)? = null
): State<Float> {
    var trigger by remember { mutableStateOf(false) }
    return animateFloatAsState(
        targetValue = if (trigger) targetValue else initialValue,
        animationSpec = animationSpec,
        visibilityThreshold = visibilityThreshold,
        finishedListener = finishedListener
    ).also {
        LaunchedEffect(Unit) {
            delay(delay)
            trigger = true
        }
    }
}

这适用于 Float,但可以很容易地转换为适合 Dp。我认为它只是将 Float 替换为 Dp