Using Jetpack Compose, I'm trying to create an animated arc. The animation that occurs involves the sweep angle of the arc decreasing in size when the value of a timer decreases. This is a pretty common animation, for example just look at the timer app on your phone.
Despite it's common use I still can't figure out how to implement it. I have successfully developed a feature where the arc sweep angle animates down but it only starts at 360 degrees if the timer is equal to 100. If the time starts at less than 100 than the sweep angle will be less than 360.
I need the sweep angle to start at 360 regardless of the starting time. Here is the code
Column(
modifier = Modifier.fillMaxSize()
){
var time by remember { mutableIntStateOf(100) }
var sweepAngle by mutableFloatStateOf(360f)
val animatedSweepAngle = animateFloatAsState(
targetValue = countDownTimerSweepAngle,
)
// Function That Starts The Timer
LaunchedEffect(time){
while (playTime > 0) {
time --
sweepAngle = (time / 100f) * 360
delay(1000)
}
}
// Time
Text(text = time.toString())
// Arc
Box(
modifier = Modifier
.size(100.dp)
.background(black)
.drawBehind{
drawArc(
color = white,
startAngle = 90f,
sweepAngle = animatedSweepAngle.value,
useCenter = false,
style = Stroke(
width = 3.dp.toPx(),
cap = StrokeCap.Round
)
)
}
)
}
If you run this code everything works fine but if you change the value of the time then it will fall apart. How do I fix this? Thanks
Using Jetpack Compose, I'm trying to create an animated arc. The animation that occurs involves the sweep angle of the arc decreasing in size when the value of a timer decreases. This is a pretty common animation, for example just look at the timer app on your phone.
Despite it's common use I still can't figure out how to implement it. I have successfully developed a feature where the arc sweep angle animates down but it only starts at 360 degrees if the timer is equal to 100. If the time starts at less than 100 than the sweep angle will be less than 360.
I need the sweep angle to start at 360 regardless of the starting time. Here is the code
Column(
modifier = Modifier.fillMaxSize()
){
var time by remember { mutableIntStateOf(100) }
var sweepAngle by mutableFloatStateOf(360f)
val animatedSweepAngle = animateFloatAsState(
targetValue = countDownTimerSweepAngle,
)
// Function That Starts The Timer
LaunchedEffect(time){
while (playTime > 0) {
time --
sweepAngle = (time / 100f) * 360
delay(1000)
}
}
// Time
Text(text = time.toString())
// Arc
Box(
modifier = Modifier
.size(100.dp)
.background(black)
.drawBehind{
drawArc(
color = white,
startAngle = 90f,
sweepAngle = animatedSweepAngle.value,
useCenter = false,
style = Stroke(
width = 3.dp.toPx(),
cap = StrokeCap.Round
)
)
}
)
}
If you run this code everything works fine but if you change the value of the time then it will fall apart. How do I fix this? Thanks
Share Improve this question asked Feb 15 at 5:00 Bob RasnerBob Rasner 2851 silver badge8 bronze badges1 Answer
Reset to default 0Your issue is that the sweepAngle calculation depends on time / 100f * 360
, which assumes time starts at 100. If time starts at a different value (e.g., 50), the initial sweepAngle is not 360.
Instead of hardcoding 100 as the full time, you should dynamically base the sweepAngle calculation on the initial time value.
I think this would resolve your issue:
@Composable
fun TimerArcScreen(startingTime: Int) {
var time by remember { mutableIntStateOf(startingTime) }
val totalTime = startingTime // Store the initial time to normalize progress
var sweepAngle by remember { mutableFloatStateOf(360f) }
// Animate the sweep angle smoothly
val animatedSweepAngle by animateFloatAsState(
targetValue = sweepAngle,
animationSpec = tween(durationMillis = 500, easing = LinearEasing)
)
// Function That Starts The Timer
LaunchedEffect(time) {
while (time > 0) {
time--
sweepAngle = (time / totalTime.toFloat()) * 360f // Normalize based on the starting time
delay(1000)
}
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
// Time Display
Text(text = time.toString(), style = MaterialTheme.typography.headlineMedium)
// Arc
Box(
modifier = Modifier
.size(150.dp)
) {
Canvas(modifier = Modifier.fillMaxSize()) {
drawArc(
color = Color.White,
startAngle = 270f, // Start from the top
sweepAngle = animatedSweepAngle,
useCenter = false,
style = Stroke(
width = 8.dp.toPx(),
cap = StrokeCap.Round
)
)
}
}
}
}