I am totally new in jetpack. I am working on a project where a shape is revolving around a centre point in a specific radius. On a button click, Shape needs to start revolving in a radius smaller than before. I am able to achieve that bit, but when shape switches, it kind of first stop for a moment, switches to smaller radius path in a vertical line manner and then start rotating again, which is kind of giving a bad look to it. How can I make this switching/transitioning of shape smooth? Below is the code I am using:
@Composable
fun AnimateObject(){
val transition = rememberInfiniteTransition()
val animatedProgress = transition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = keyframes {
durationMillis = 3000
0.0f at 0 with LinearEasing
1.0f at 3000 with LinearEasing
},
repeatMode = RepeatMode.Restart
)
)
val outerCircleRadius = 450f
val innerCircleRadius = 250f
var outerCircleCenter = Offset(x = 0f, y = 0f)
var innerCircleCenter = Offset(x = 0f, y = 0f)
var playerXOffset = remember { mutableStateOf(0f) }
var playerYOffset = remember { mutableStateOf(0f) }
var isInOuterCircle = remember { mutableStateOf(true) }
val angle = animatedProgress.value * 360f
val radians = Math.toRadians(angle.toDouble())
Canvas(modifier = Modifier.size(200.dp), onDraw = {
outerCircleCenter = Offset(size.width / 2, size.height / 2)
innerCircleCenter = Offset(size.width / 2, size.height / 2)
CreateCirlcePath(radius = outerCircleRadius, center = outerCircleCenter, drawScope = this)
CreateCirlcePath(radius = innerCircleRadius, center = innerCircleCenter, drawScope = this)
if (isInOuterCircle.value == true) {
playerXOffset.value = outerCircleCenter.x + outerCircleRadius * cos(radians).toFloat()
playerYOffset.value = outerCircleCenter.y + outerCircleRadius * sin(radians).toFloat()
} else {
playerXOffset.value = innerCircleCenter.x + innerCircleRadius * Math.cos(radians).toFloat()
playerYOffset.value = innerCircleCenter.y + innerCircleRadius * Math.sin(radians).toFloat()
}
print("Offset values, ${playerXOffset.value} ")
// Draw the moving shape (circle in this case)
drawCircle(
color = Color.Blue,
center = Offset(playerXOffset.value, playerYOffset.value),
radius = 16.dp.toPx()
)
})
Row {
val buttonTitle = remember { mutableStateOf("Click Me") }
Button(
onClick = {
buttonTitle.value = "Clicked"
isInOuterCircle.value = !isInOuterCircle.value
},
modifier = Modifier.size(width = 150.dp, height = 50.dp)
) {
Text(text = buttonTitle.value)
}
}
}
I am experiencing a glitchy transition from one circle path to another. Here is the link to demonstrate how does it look:
I want this transition to be smooth in a way that when transition is happening from one circle to another, shape continues its motion and during switching, it lands on the other circle path in a slanted manner (or like a tangent). Any help would be appreciated! Thanks
You can linearly interpolate between inner and outer circles using lerp
function.
https://en.wikipedia.org/wiki/Linear_interpolation
I used rotate
function of Canvas
while only interpolating trajectory of circle and results is
@Preview
@Composable
private fun RadiusChangeLerpAnimationTes() {
val outerCircleRadius = 450f
val innerCircleRadius = 250f
var isOut by remember {
mutableStateOf(false)
}
val transition = rememberInfiniteTransition(label = "")
val angle by transition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = keyframes {
durationMillis = 3000
0.0f at 0 with LinearEasing
360f at 3000 with LinearEasing
},
repeatMode = RepeatMode.Restart
), label = ""
)
val progress by animateFloatAsState(
if (isOut) 1f else 0f,
animationSpec = tween(durationMillis = 700, easing = LinearEasing),
label = "distance"
)
val distance = remember(progress) {
lerp(innerCircleRadius, outerCircleRadius, progress)
}
var position by remember {
mutableStateOf(Offset.Unspecified)
}
Column {
Canvas(
modifier = Modifier.fillMaxWidth().aspectRatio(1f)
) {
drawCircle(
color = Color.Blue,
radius = outerCircleRadius,
style = Stroke(2.dp.toPx())
)
drawCircle(
color = Color.Blue,
radius = innerCircleRadius,
style = Stroke(2.dp.toPx())
)
rotate(angle) {
drawCircle(
color = Color.Red,
radius = 50f,
center = Offset(center.x + distance, center.y)
)
}
val angleInRadians = angle * DEGREE_TO_RAD
position = Offset(
center.x + distance * cos(angleInRadians), center.y + distance * sin(angleInRadians)
)
}
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
isOut = isOut.not()
}
) {
Text("Out $isOut")
}
if (position != Offset.Unspecified) {
Text("Position: $position")
}
}
}
private const val DEGREE_TO_RAD = (Math.PI / 180f).toFloat()