Compose: Infinite animation with variable speed

1k Views Asked by At

I have this code to get an infinite rotation animation:

@Composable
fun RotatingObject(rpm: Int) {
    val infiniteTransition = rememberInfiniteTransition()
    val rotation by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(
            animation = tween(
                durationMillis = 1000,
                easing = LinearEasing
            )
        )
    )

    Surface(
        Modifier
            .size(100.dp)
            .graphicsLayer { rotationZ = rotation},
        color = Color.Gray
    ) {}
}

I want the rpm parameter to define the number of revolutions per minute the object should make while spinning. I have tried setting durationMillis to 60000 / rpm but the speed stays the same after rpm changes.

How can I get the speed to change after the initial composition?

Requirements
The rotation angle should not jump or jitter - it should always continually change based on the current rpm.
The solution should be friendly to animated rpm values to allow for smoothly changing speed over time.
I would prefer a solution that doesn't cause avoidable recompositions.


Edit:

After trying countless different things, each of which had something wrong with it, I'm going to share the least wrong outcome.

@Composable
fun RotatingObject(rpm: Int) {
    var rotation by remember { mutableStateOf(0f) }
    val infiniteTransition = rememberInfiniteTransition()
    val ticker by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(
            animation = tween(
                durationMillis = 1000,
                easing = LinearEasing
            )
        )
    )

    LaunchedEffect(ticker) {
        rotation += 0.1f * rpm
    }

    Surface(
        Modifier
            .size(100.dp)
            .graphicsLayer { rotationZ = rotation},
        color = Color.Gray
    ) {}
}

I track the rotation value as state and manually change it inside a LaunchedEffect. The relaunching of the effect is controlled by the infinite transition. This makes the rotation update at the expected frame rate. 0.1f * rpm is the conversion of rpm to 'degrees per frame' at 60Hz.

So, this behaves as I want it to, however, in order to get here I made an assumption that I feel is not safe to make. This code requires that the effect is relaunched at a constant frequency of 60Hz. Since I didn't see this defined anywhere, this might just be the case on my system.

Also, if I do more operations inside the effect, it sometimes decides not to update the rotation at all, meaning that this is not a reliable way to do this. Therefore I feel that this is not a correct solution and I'm afraid it may not behave the same on different systems or at different times even. I currently have no choice but to use this, so if you have any ideas or suggestions, please share.

0

There are 0 best solutions below