如何用Jetpack Compose实现计时器递减时扫角从360开始的圆弧动画
问题分析与解决方案
你的核心问题是扫角计算依赖固定的100作为分母,导致只有初始时间为100时,扫角才会从360°开始。另外代码里还有几处变量名不匹配的bug(比如countDownTimerSweepAngle、playTime都是未定义的变量),需要一并修正。
关键修复点:
- 保存初始时间值,用当前时间与初始时间的比例计算扫角,而非固定除以100
- 修正变量名不匹配的错误
- 调整LaunchedEffect的触发逻辑,避免计时器重复启动
完整修正代码:
Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { // 可自定义初始时间 val initialTime = 60 var time by remember { mutableIntStateOf(initialTime) } var isTimerRunning by remember { mutableStateOf(true) } var sweepAngle by mutableFloatStateOf(360f) val animatedSweepAngle = animateFloatAsState( targetValue = sweepAngle, animationSpec = tween(durationMillis = 1000) // 匹配1秒倒计时的动画时长 ) // 启动计时器 LaunchedEffect(isTimerRunning) { if (isTimerRunning) { while (time > 0) { delay(1000) time-- // 用当前时间/初始时间的比例计算扫角,确保初始扫角始终为360° sweepAngle = (time.toFloat() / initialTime) * 360f } isTimerRunning = false } } // 显示剩余时间 Text( text = time.toString(), fontSize = 32.sp, color = Color.White, modifier = Modifier.padding(bottom = 20.dp) ) // 动画圆弧 Box( modifier = Modifier .size(100.dp) .background(Color.Black) .drawBehind { drawArc( color = Color.White, startAngle = 90f, sweepAngle = animatedSweepAngle.value, useCenter = false, style = Stroke( width = 3.dp.toPx(), cap = StrokeCap.Round ) ) } ) }
说明:
- 初始时间保存:用
initialTime定义初始倒计时时长,remember保存当前时间time的初始值为initialTime,确保比例计算的基准正确。 - 扫角计算逻辑:
(time.toFloat() / initialTime) * 360f保证初始时time=initialTime,扫角为360°;每减少1秒,扫角按比例递减,直到时间归0时扫角为0°。 - 动画同步:设置
animationSpec = tween(durationMillis = 1000),让扫角动画的时长与1秒倒计时的间隔匹配,过渡更平滑。 - 计时器逻辑优化:用
isTimerRunning控制计时器启动,避免LaunchedEffect因time变化重复触发,确保计时器只运行一次。
内容的提问来源于stack exchange,提问作者Bob Rasner
相关产品推荐
相关产品推荐

