Issue
I was trying to do a compass in jetpack compose. But I faced a problem with animating it.
I have a @Composable
that takes user phone rotation and rotate compass image in opposite direction. I use animateFloatAsState
like this:
val angle: Float by animateFloatAsState(
targetValue = -rotation, \\ rotation is retrieved as argument
animationSpec = tween(
durationMillis = UPDATE_FREQUENCY, \\ rotation is retrieved with this frequency
easing = LinearEasing
)
)
Image(
modifier = Modifier.rotate(angle),
// rest of the code for image
)
Everything looks fine but the problem occurs when rotation
is changed from 1
to 359
or in the opposite way. Animation doesn't rotate 2
degrees to the left but goes 358
degrees to the right which looks bad. Is there any way to make rotate animation that would use the shortest way?
Solution
I ended up doing this:
val (lastRotation, setLastRotation) = remember { mutableStateOf(0) } // this keeps last rotation
var newRotation = lastRotation // newRotation will be updated in proper way
val modLast = if (lastRotation > 0) lastRotation % 360 else 360 - (-lastRotation % 360) // last rotation converted to range [-359; 359]
if (modLast != rotation) // if modLast isn't equal rotation retrieved as function argument it means that newRotation has to be updated
{
val backward = if (rotation > modLast) modLast + 360 - rotation else modLast - rotation // distance in degrees between modLast and rotation going backward
val forward = if (rotation > modLast) rotation - modLast else 360 - modLast + rotation // distance in degrees between modLast and rotation going forward
// update newRotation so it will change rotation in the shortest way
newRotation = if (backward < forward)
{
// backward rotation is shorter
lastRotation - backward
}
else
{
// forward rotation is shorter (or they are equal)
lastRotation + forward
}
setLastRotation(newRotation)
}
val angle: Float by animateFloatAsState(
targetValue = -newRotation.toFloat(),
animationSpec = tween(
durationMillis = UPDATE_FREQUENCY,
easing = LinearEasing
)
)
So basically I remembered the last rotation and based on this when a new rotation comes in I check which way (forward or backward) is shorter and then use it to update the target value.
Answered By - iknow
Answer Checked By - Marie Seifert (JavaFixing Admin)