如何创建如下所示的弧形进度条动画

animated progress

目前,我已经使用Canvas绘制了一条圆弧,并使用AnimateFloatAsState API向进度栏添加了动画.但是第二张照片不是我想要的.

[My current implementation]

My current implementation

// e.g. oldScore = 100f  newScore = 350f
// Suppose 250 points are into one level

@Composable
fun ArcProgressbar(
    modifier: Modifier = Modifier,
    oldScore: Float,
    newScore: Float,
    level: String,
    startAngle: Float = 120f,
    limitAngle: Float = 300f,
    thickness: Dp = 8.dp
) {

    var value by remember { mutableStateOf(oldScore) }

    val sweepAngle = animateFloatAsState(
        targetValue = (value / 250) * limitAngle,  // convert the value to angle
        animationSpec = tween(
            durationMillis = 1000
        )
    )

    LaunchedEffect(Unit) {
        delay(1500)
        value = newScore
    }

    Box(modifier = modifier.fillMaxWidth()) {

        Canvas(
            modifier = Modifier
                .fillMaxWidth(0.45f)
                .padding(10.dp)
                .aspectRatio(1f)
                .align(Alignment.Center),
            onDraw = {
                // Background Arc
                drawArc(
                    color = Gray100,
                    startAngle = startAngle,
                    sweepAngle = limitAngle,
                    useCenter = false,
                    style = Stroke(thickness.toPx(), cap = StrokeCap.Square),
                    size = Size(size.width, size.height)
                )

                // Foreground Arc
                drawArc(
                    color = Green500,
                    startAngle = startAngle,
                    sweepAngle = sweepAngle.value,
                    useCenter = false,
                    style = Stroke(thickness.toPx(), cap = StrokeCap.Square),
                    size = Size(size.width, size.height)
                )
            }
        )
        
        Text(
            text = level,
            modifier = Modifier
                .fillMaxWidth(0.125f)
                .align(Alignment.Center)
                .offset(y = (-10).dp),
            color = Color.White,
            fontSize = 82.sp
        )

        Text(
            text = "LEVEL",
            modifier = Modifier
                .padding(bottom = 8.dp)
                .align(Alignment.BottomCenter),
            color = Color.White,
            fontSize = 20.sp
        )
    }
}

如果进度百分比超过100%,我如何从头开始制作动画,就像gif中的那个一样.有谁有什么主意吗?谢谢!

推荐答案

我在您的代码中做了一些更改,以利用Animatable,所以我们总是从snap开始,然后再动画到target的值.我们还在这里省go 了计算,因为我们只想在每次分数更新时填充整个进度,在我们的例子中是300(limitAngle),并使用newScore状态作为LaunchedEffect中的key来在每次它增加时触发动画.不要介意+30的增量,这只是一个任意值,您可以在不影响动画的情况下进行更改.

@Composable
fun ArcProgressbar(
    modifier: Modifier = Modifier,
    newScore: Float,
    level: String,
    startAngle : Float = 120f,
    limitAngle: Float = 300f,
    thickness: Dp = 8.dp
) {

    val animateValue = remember { Animatable(0f) }

    LaunchedEffect(newScore) {
        if (newScore > 0f) {
            animateValue.snapTo(0f)
            delay(10)
            animateValue.animateTo(
                targetValue = limitAngle,
                animationSpec = tween(
                    durationMillis = 1000
                )
            )
        }
    }

    Box(modifier = modifier.fillMaxWidth()) {

        Canvas(
            modifier = Modifier
                .fillMaxWidth(0.45f)
                .padding(10.dp)
                .aspectRatio(1f)
                .align(Alignment.Center),
            onDraw = {
                // Background Arc
                drawArc(
                    color = Color.Gray,
                    startAngle = startAngle,
                    sweepAngle = limitAngle,
                    useCenter = false,
                    style = Stroke(thickness.toPx(), cap = StrokeCap.Square),
                    size = Size(size.width, size.height)
                )

                // Foreground Arc
                drawArc(
                    color = Color.Green,
                    startAngle = startAngle,
                    sweepAngle = animateValue.value,
                    useCenter = false,
                    style = Stroke(thickness.toPx(), cap = StrokeCap.Square),
                    size = Size(size.width, size.height)
                )
            }
        )

        Column {
            Text(
                text = level,
                modifier = Modifier
                    .fillMaxWidth(0.125f)
                    .offset(y = (-10).dp),
                color = Color.Gray,
                fontSize = 82.sp
            )

            Text(
                text = "LEVEL",
                modifier = Modifier
                    .padding(bottom = 8.dp),
                color = Color.Gray,
                fontSize = 20.sp
            )

            Text(
                text = "Score ( $newScore ) ",
                modifier = Modifier
                    .padding(bottom = 8.dp),
                color = Color.Gray,
                fontSize = 20.sp
            )
        }
    }
}

示例用法:

@Composable
fun ScoreGenerator() {

    var newScore by remember {
        mutableStateOf(0f)
    }

    Column {
        Button(onClick = {
            newScore += 30f
        }) {
            Text("Add Score + 30")
        }

        ArcProgressbar(
            newScore = newScore,
            level = ""
        )
    }
}

enter image description here

Android相关问答推荐

如何阻止Gradle在编译时生成app-metadata.properties

如何将两个变量传递给Nav主机,然后将其传递给另一个屏幕?

我无法在底部导航栏中正确导航-Android底部导航视图

Jetpack Compose:根据盒子中的最大视图固定宽度和高度

是否可以附加事件处理程序,如onClick,拖动到Canvas Composable中绘制的内容,或使用drawBehind修饰符?

android回收器查看点击事件无响应

无法插入 LayoutNode@cc72396 子级,因为它已有父级

如何将DrawableId参数传递给XML布局?

如何在Android Studio中禁用文件中的Github用户名引用?

为一组闪烁的可组合项制作动画,控制同步/定时

Koin Android-KMM:我有嵌套范围但注入不起作用

Jetpack Compose 动画性能问题

如何在组件之间导航

根据 Jetpack Compose 中的生命周期正确处理变量/函数

在 Kotlin 中打开新片段时如何对当前片段应用更改?

Hilt 依赖注入重复绑定错误

为 AlertDialog 的消息文本设置自定义字体

Jetpack Compose:mutableStateOf 不随流量更新

我应该使用 Bluetooth Classic 还是 Bluetooth LE 与我的移动应用程序通信?

将生成的 AAR 与 Composables 一起使用时未解决的参考