I am trying to achieve translate animation in Jetpack compose but i am not able to find Suitable Source for this.Can any one Help me to achieve translate Animation in jetpack compose in which i can set start and edning positionl Manually..
The alternative of translate animation in jetpack compose is OFFSET ANIMATION
yes, I was able to achieve this through offset animation.I am sharing the code below with comments in detail so it will be easier for the reader to understand it
// Courtine Scope to Run the animation in thread
val coroutineScope = rememberCoroutineScope()
val offsetX = remember { Animatable(0f) }
val offsetY = remember { Animatable(0f) }
Image(
painter = rememberDrawablePainter(
ContextCompat.getDrawable(
context,
R.drawable.image
)
),
contentDescription = "s", contentScale = ContentScale.Crop,
modifier = Modifier
.offset {
IntOffset(
offsetX.value.toInt(),
offsetY.value.toInt()
)
}
.width(300.dp)
.height(300.dp)
)
//Finally run the animation on the Click of your button or whenever you wants to start it...
coroutineScope.launch {
launch {
offsetXFirst.animateTo(
targetValue = targetValue,
animationSpec = tween(
durationMillis = 2000,
delayMillis = 0))}
launch {
offsetYFirst.animateTo(
targetValue = size.height.toFloat(),
animationSpec = tween(
durationMillis = 2000,
delayMillis = 0))}
}
Using the offset was my solution as well. However used animateDpAsState instead. There a tab indicator is moved on the x axis:
val offsetState = animateDpAsState(targetValue = targetPositionDp)
Box(modifier = Modifier
.offset(offsetState.value, 0.dp)
.background(color = Color.Red)
.size(tabWidth, tabHeight))
Related
I'm new to Jetpack Compose, and I'm trying to rotate the home screen with animation when the menu button is tapped. It works fine 3-5 times, but suddenly it lags like crazy and I don't know why? Is this a bug, or am I doing something wrong?
var isOpen by remember { mutableStateOf(false) }
val transition = updateTransition(targetState = isOpen, "Menu")
val rotation by transition.animateFloat(
transitionSpec = { spring(0.4f, Spring.StiffnessLow) },
label = "MenuRotation",
targetValueByState = { if (it) -30f else 0f }
)
val scale by transition.animateFloat(
label = "MenuScale",
targetValueByState = { if (it) 0.9f else 1f }
)
val translateX by transition.animateFloat(
transitionSpec = { tween(400) },
label = "MenuTranslation",
targetValueByState = { if (it) 536f else 0f }
)
Box(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
cameraDistance = density * 10f
rotationY = rotation
scaleX = scale
translationX = translateX
}
) {
HomeScreen()
}
Box {
DefaultButton(
onClick = { isOpen = ! isOpen },
modifier = Modifier
.padding(16.dp)
.padding(top = 32.dp)
.shadow(blur = 8.dp, radius = 16.dp)
.size(32.dp, 32.dp),
shape = Shapes.large,
) {
Icon(
imageVector = if (isOpen) Icons.Filled.Close else Icons.Filled.Menu,
contentDescription = "Menu",
tint = Color.Black,
)
}
}
Update #1
I found this in the logcat
Skipped 52 frames! The application may be doing too much work on its main thread.
Davey! duration=1067ms; Flags=0, FrameTimelineVsyncId=2511155, IntendedVsync=4294436921331, Vsync=4294870254647, InputEventId=0, HandleInputStart=4294871069349, AnimationStart=4294871070287, PerformTraversalsStart=4294871939089, DrawStart=4294872039558, FrameDeadline=4294486921330, FrameInterval=4294870971954, FrameStartTime=41666666, SyncQueued=4294872645860, SyncStart=4295312217578, IssueDrawCommandsStart=4295312304089, SwapBuffers=4295937520703, FrameCompleted=4295944298047, DequeueBufferDuration=5729, QueueBufferDuration=166719, GpuCompleted=4295944298047, SwapBuffersCompleted=4295937862943, DisplayPresentTime=4237530536663, CommandSubmissionCompleted=4295937520703,
Update #2
The animation works flawlessly when I comment out all text components and vice versa. So what's wrong with the text components?
Please check the HomeScreen composable, component recomposition counts。
I suspect that HomeScreen reorganizes too many times。
You can just replace #Composable HomeScreen with an #Composeable Image verify。
I'm creating a 2D game with simple animations in jetpack compose. The game is simple, a balloon will float on top, a cannon will shoot arrow towards balloon. If the arrow hits balloon, player gets a point. I'm able to animate all of the above things but one. How would I detect when the arrow and balloon areas intersect?
The arrow is moving upwards with animation, also the balloon is animating side-ways. I want to capture the point at which the arrow touches the balloon. How would I proceed with this?
CODE:
Balloon Composable
#Composable
fun Balloon(modifier: Modifier, gameState: GameState) {
val localConfig = LocalConfiguration.current
val offsetX by animateDpAsState(
targetValue = if (gameState == GameState.START) 0.dp else (localConfig.screenWidthDp.dp - 80.dp),
infiniteRepeatable(
animation = tween((Math.random() * 1000).toInt(), easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
val offsetY by animateDpAsState(
targetValue = if (gameState == GameState.START) 0.dp else (localConfig.screenHeightDp.dp / 3),
infiniteRepeatable(
animation = tween((Math.random() * 1000).toInt(), easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
Box(
modifier = modifier
.offset(offsetX, offsetY)
.size(80.dp)
.background(Color.Red, shape = CircleShape),
contentAlignment = Alignment.Center,
) {
Text(text = "D!!")
}
}
Cannon Composable:
#Composable
fun ShootBalloon(modifier: Modifier) {
val localConfig = LocalConfiguration.current
var gameState by remember { mutableStateOf(GameState.START) }
val offsetX by animateDpAsState(
targetValue = if (gameState == GameState.START) 0.dp else (localConfig.screenWidthDp.dp - 50.dp),
infiniteRepeatable(
animation = tween(2000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
val offsetY by animateDpAsState(
targetValue = if (gameState == GameState.START) 0.dp else (-(localConfig.screenHeightDp.dp - 50.dp)),
infiniteRepeatable(
animation = tween(500, easing = LinearEasing),
repeatMode = RepeatMode.Restart
)
)
Column(
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.Start
) {
if (gameState == GameState.START) {
Button(onClick = { gameState = GameState.PLAYING }) { Text(text = "Play") }
}
Box(
modifier = Modifier
.size(50.dp)
.offset(x = offsetX)
.background(Color.Green),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(R.drawable.ic_baseline_arrow_upward_24),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.offset(y = offsetY)
.onGloballyPositioned { layoutCoordinates ->
val offset = layoutCoordinates.positionInRoot()
}
)
}
}
}
I'm animating following things,
The cannon box moves side ways, the arrow image moves upwards.
The balloon has random movement.
Add a ticker and on each tick check the global coordinate of arrow and offsets of balloon. If values are same, it means they hit on another
I want to achieve a translation animation which will run continuously till some values from sever will stop it. Here is the image of what I need to achieve :
you can see a white vertical line over this which is an image. I need to translate it from start to end continuosly [not back and forth]
This layout has a Box which is gray colored and inside it there is a LinearProgressIndicator. So this progress can be anything like 50%,80% etc. And this image can only animate till the progress bar.
See the code below :
Box(modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.constrainAs(main_progress_bar) {
top.linkTo(parent.top, 16.dp)
start.linkTo(parent.start, 16.dp)
end.linkTo(parent.end, 16.dp)
width = Dimension.fillToConstraints
}
.background(getAppColors().gray, RoundedCornerShape(10.dp))
) {
val offsetAnimation: Dp by animateDpAsState(
100.dp,
infiniteRepeatable(
animation = tween(
800,
easing = LinearEasing,
delayMillis = 1_500,
),
repeatMode = RepeatMode.Restart,
)
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth((viewModel.mainPercentage / 100))
.height(50.dp)
.clip(
RoundedCornerShape(
topStart = 10.dp,
topEnd = 0.dp,
bottomStart = 10.dp,
bottomEnd = 0.dp
)
),
backgroundColor = getAppColors().gray,
color = getAppColors().greenColor,
progress = 1f,
)
Image(
painter = rememberDrawablePainter(
ContextCompat.getDrawable(
context,
R.drawable.ic_animation_icon
)
),
contentDescription = "image",
modifier = Modifier
.absoluteOffset(x = offsetAnimation)
)
}
This code is not helping working as expected. I tried with some other answer's but I can't achieve exactly what I need. Tried this It is helpful but how do I get animation end callback here so I can restart it or how do I control animation from viewModel itself? That's the main problem I am facing.
Solution based on official sample:
val infiniteTransition = rememberInfiniteTransition()
val offsetAnimation by infiniteTransition.animateValue(
initialValue = 0.Dp,
targetValue = 100.Dp,
typeConverter = Dp.VectorConverter,
animationSpec = infiniteRepeatable(
animation = tween(800, easing = LinearEasing, delayMillis = 1_500),
repeatMode = RepeatMode.Restart
)
)
I'm not sure how it will work if targetValue changes..
How to do my image to rotate infinitely?
This is my code but animation does not work
val angle: Float by animateFloatAsState(
targetValue = 360F,
animationSpec = infiniteRepeatable(
tween(2000))
)
Image(
painter = painterResource(R.drawable.sonar_scanner),
"image",
Modifier
.fillMaxSize()
.rotate(angle),
contentScale = ContentScale.Fit
)
You can use the InfiniteTransition using rememberInfiniteTransition.
Something like
val infiniteTransition = rememberInfiniteTransition()
val angle by infiniteTransition.animateFloat(
initialValue = 0F,
targetValue = 360F,
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = LinearEasing)
)
)
Just a note.
Instead of using
Modifier.rotate(angle)
You can use
Modifier
.graphicsLayer {
rotationZ = angle
}
As you can check in the doc:
Prefer this version when you have layer properties backed by a androidx.compose.runtime.State or an animated value as reading a state inside block will only cause the layer properties update without triggering recomposition and relayout.
You should use the pre-defined API for infinite animations for such use cases in my opinion.
val infiniteTransition = rememberInfiniteTransition()
val angle by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = keyframes {
durationMillis = 1000
}
)
)
I know I can use the AnimatedVisibility Composable function and achieve slide-in animation for the visibility animation, but what I want to achieve is when one layout is in entering animation the other in the exit animation, something similar to the image below.
NB : I know that I should use Navigation compose for different screens and that animation between destinations is still under development, but I want to achieve this on the content of a part of screen, similar to CrossFade Animation.
As you mentioned, this animation should be implemented by the Navigation Library and there's a ticket opened to that.
Having that in mind, I'm leaving my answer here and I hope it helps...
I'll break it in three parts:
The container:
#Composable
fun SlideInAnimationScreen() {
// I'm using the same duration for all animations.
val animationTime = 300
// This state is controlling if the second screen is being displayed or not
var showScreen2 by remember { mutableStateOf(false) }
// This is just to give that dark effect when the first screen is closed...
val color = animateColorAsState(
targetValue = if (showScreen2) Color.DarkGray else Color.Red,
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
)
Box(Modifier.fillMaxSize()) {
// Both Screen1 and Screen2 are declared here...
}
}
The first screen just do a small slide to create that parallax effect. I'm also changing the background color from Red to Dark just to give this overlap/hide/dark effect.
// Screen 1
AnimatedVisibility(
!showScreen2,
modifier = Modifier.fillMaxSize(),
enter = slideInHorizontally(
initialOffsetX = { -300 }, // small slide 300px
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing // interpolator
)
),
exit = slideOutHorizontally(
targetOffsetX = { -300 }, =
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
)
) {
Box(
Modifier
.fillMaxSize()
.background(color.value) // animating the color
) {
Button(modifier = Modifier.align(Alignment.Center),
onClick = {
showScreen2 = true
}) {
Text(text = "Ok")
}
}
}
The second is really sliding from the edges.
// Screen 2
AnimatedVisibility(
showScreen2,
modifier = Modifier.fillMaxSize(),
enter = slideInHorizontally(
initialOffsetX = { it }, // it == fullWidth
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
),
exit = slideOutHorizontally(
targetOffsetX = { it },
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
)
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Blue)
) {
Button(modifier = Modifier.align(Alignment.Center),
onClick = {
showScreen2 = false
}) {
Text(text = "Back")
}
}
}
Here is the result:
After digging in the code of CrossFade I implemented a similar one for the cross slide and it enables reverse animation for when pressing backButton
Here it is : https://gist.github.com/DavidIbrahim/5f4c0387b571f657f4de976822c2a225
Usage Example
#Composable
fun CrossSlideExample(){
var currentPage by remember { mutableStateOf("A") }
CrossSlide(targetState = currentPage, reverseAnimation: Boolean = false) { screen ->
when (screen) {
"A" -> Text("Page A")
"B" -> Text("Page B")
}
}
}
Now we have an official solution for this. Lately Google Accompanist has added a library which provides Compose Animation support for Jetpack Navigation Compose..
https://github.com/google/accompanist/tree/main/navigation-animation
As of now, we don't have anything comparable to Activity Transitions in Compose.
Jetpack should be working on them I hope. An awful lot of transition APIs are either internal or private to Compose Library so implementing a fine one is harder.
If for production, Kindly use Activity/Fragment with Navigation Host. If not use AnimatedVisibility to slide without the navigation component.
https://issuetracker.google.com/issues/172112072