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..
Related
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 need to do a parallax implementation and in addition when user is scrolling down actionbar should appear. I am following this tutorial:
https://proandroiddev.com/parallax-in-jetpack-compose-bf521244f49
There is an implementation:
#Composable
fun HeaderBarParallaxScroll() {
val scrollState = rememberScrollState()
Box {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(scrollState),
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(500.dp)
.background(Color.White)
.graphicsLayer {
Log.e(
"scroll",
"${scrollState.value.toFloat()}, max = ${scrollState.maxValue}, ratio = ${(scrollState.value.toFloat() / scrollState.maxValue)}"
)
alpha = 1f - ((scrollState.value.toFloat() / scrollState.maxValue) * 1.5f)
translationY = 0.5f * scrollState.value
},
contentAlignment = Alignment.Center
) {
Image(
painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "tiger parallax",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
}
repeat(100) {
Text(
text = "MyText",
modifier = Modifier.background(
Color.White
),
style = TextStyle(
color = Color.Red,
fontSize = 24.sp
)
)
}
}
Box(
modifier = Modifier
.alpha(min(1f, (scrollState.value.toFloat() / scrollState.maxValue) * 5f))
.fillMaxWidth()
.height(60.dp)
.background(Color.Yellow),
contentAlignment = Alignment.CenterStart
) {
Text(
text = "Header bar",
modifier = Modifier.padding(horizontal = 16.dp),
style = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.W900,
color = Color.Black
)
)
}
}
}
Looks like everything is working as expected, however, if I change the value repeat in this block
repeat(100) {
Text(
text = "MyText",
modifier = Modifier.background(
Color.White
),
style = TextStyle(
color = Color.Red,
fontSize = 24.sp
)
)
}
instead of 100 -> 1000 parallax working slower and in order for the actionbar to appear I need to scroll like half of the list, so to put it differently parallax responsiveness depends on how many items (height) are in the content. Eg: if it is 100 it works as expected, however, if it is 1000 it works much slower...
How to make it work properly?
Don't calculate the ratio based on the scroll.maxValue, but use the screen height as the max bound. Get it in pixels like this.
val screenHeight = with (LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.roundToPx() }
These are all composables, so you'll need to call this logic within a Composable scope. After the initialization, of course, the variable can be used anywhere, even in non-composable scopes.
I wrote the composable below (shown as dialog). When viewState.errorCode != 0, another composable is shown. This all works fine, but the height of the box doesn't adjust when the new composable becomes visible.This results in an 'invisible overflow' whereby a number of items are no longer visible. Is there a way to make the box dynamic so that it adjusts in height when a new element becomes visible?
Box(modifier = Modifier
.clip(RoundedCornerShape(4.dp))) {
Column(
modifier = Modifier
.background(MaterialTheme.colors.onPrimary, MaterialTheme.shapes.large)
.padding(12.dp)
) {
Text(stringResource(R.string.verify_hint, user.email).parseBold(), fontSize = 18.textDp, fontFamily = SourceSans)
if (viewState.errorCode != 0) {
AlertMessage(message = stringResource(id = viewState.errorCode), color = errorColor, padding = PaddingValues(top = 12.dp))
}
TextField(
value = code,
onValueChange = { code = it },
label = { Text(stringResource(R.string.verification_code)) },
colors = TextFieldDefaults.textFieldColors(backgroundColor = textFieldColor),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp, bottom = 12.dp)
)
NMButton(
onClick = { viewModel.verify(user, code, verifyLogin, language = context.getLanguageBasedOnConfiguration()) },
modifier = Modifier
.fillMaxWidth()
.padding(start = 0.dp, end = 0.dp),
icon = R.drawable.ic_badge_check_solid,
label = stringResource(R.string.verify)
)
}
if (viewState.loading) {
Loader()
}
}
Yes there is.
You can use animateContentSize on your box modifier
then you declare an animationSpec like below:
Box(modifier = Modifier
.animateContentSize(
animationSpec = tween(
durationMillis = 300,
easing = LinearOutSlowInEasing
)
).clip(RoundedCornerShape(4.dp))
)
Now if the AlertMessage appears it will animate the size of the box.
You can also create expandable cards with this approach.
This video may help you too.
I am trying to achieve the blur effect like highlighted in this post:
I'm using Jetpack Compose:
#Composable
fun MainApp() {
val linearGradientBrush = Brush.linearGradient(
colors = listOf(
Color(0xFF5995EE),
Color(0xFFB226E1),
Color(0xFFE28548)
),
start = Offset(Float.POSITIVE_INFINITY, 0f),
end = Offset(0f, Float.POSITIVE_INFINITY),
)
val transparentGradientBrush = Brush.linearGradient(
colors = listOf(
Color(0x66FFFFFF),
Color(0x1AFFFFFF)
)
)
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Box() {
Card(
modifier = Modifier
.size(150.dp)
// .blur(10.dp, edgeTreatment = BlurredEdgeTreatment.Unbounded)
.clip(shape = CircleShape)
.background(linearGradientBrush),
backgroundColor = Color.Transparent,
elevation = 0.dp,
) {
}
Card(
modifier = Modifier
.size(150.dp)
.offset(x = -75.dp, y = 40.dp)
.blur(5.dp, edgeTreatment = BlurredEdgeTreatment.Unbounded)
.clip(RoundedCornerShape(30.dp))
.background(transparentGradientBrush),
backgroundColor = Color.Transparent,
elevation = 0.dp
) {
}
}
}
}
However, the output I get is this:
How do I achieve the effect from the tweet above the layer in front also blurs the layer behind? Is there a way to achieve this?
https://github.com/x3rocode/xblur-compose/tree/main
I made simple realtime compose blur!
try this.
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))