Skip to content

Instantly share code, notes, and snippets.

@rodrigomartind
Created June 6, 2023 21:35
Show Gist options
  • Save rodrigomartind/d0a2926cd8552d46f04e33ce0bce37ce to your computer and use it in GitHub Desktop.
Save rodrigomartind/d0a2926cd8552d46f04e33ce0bce37ce to your computer and use it in GitHub Desktop.
The Art of Small Animations in Android with Jetpack Compose
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Notifications
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
@Preview
@Composable
fun BasicInfiniteCompose() {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
contentAlignment = Alignment.Center
) {
RowItem()
// NotificationIconAnimated()
// BadgeAnimated()
// CircleImageBorderAnimated()
// DraggableRowItem()
}
}
@Composable
fun CircleImageBorderAnimated() {
val colors = listOf(
Color.fromHex("#405DE6"),
Color.fromHex("#C13584"),
Color.fromHex("#FD1D1D"),
Color.fromHex("#FFDC80")
)
var gradientBrush by remember {
mutableStateOf(
Brush.horizontalGradient(
colors = colors,
startX = -10.0f,
endX = 400.0f,
tileMode = TileMode.Repeated
)
)
}
val value by rememberInfiniteTransition().animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 1000,
easing = LinearEasing
)
)
)
Box(modifier = Modifier
.drawBehind {
rotate(value) {
drawCircle(
gradientBrush, style = Stroke(width = 12.dp.value)
)
}
}
.size(125.dp)
)
}
@Composable
fun BadgeAnimated() {
val value by rememberInfiniteTransition().animateFloat(
initialValue = 0.5f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 600,
easing = LinearEasing
),
repeatMode = RepeatMode.Reverse
)
)
Box(modifier = Modifier
.graphicsLayer {
scaleX = value
scaleY = value
}
.size(25.dp)
.clip(CircleShape)
.background(Color.Red)
)
}
@Composable
fun NotificationIconAnimated() {
val value by rememberInfiniteTransition().animateFloat(
initialValue = 25f,
targetValue = -25f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 600,
easing = LinearEasing
),
repeatMode = RepeatMode.Reverse
)
)
Icon(
imageVector = Icons.Outlined.Notifications,
contentDescription = "Notification Icon",
modifier = Modifier
.graphicsLayer(
transformOrigin = TransformOrigin(
pivotFractionX = 0.5f,
pivotFractionY = 0.0f,
),
rotationZ = value
)
)
}
@Composable
fun RowItem() {
val value by rememberInfiniteTransition().animateFloat(
initialValue = 1.dp.value,
targetValue = 16.dp.value,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 1000,
),
repeatMode = RepeatMode.Reverse
)
)
Card(
modifier = Modifier
.padding(all = 16.dp)
.fillMaxWidth()
.height(100.dp),
elevation = value.dp,
shape = RoundedCornerShape(12.dp),
backgroundColor = Color.White
) {
Box(contentAlignment = Alignment.Center) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Rodrigo Martin Dominguez")
Text(text = "Android Engineer")
}
}
}
}
@Composable
fun DraggableRowItem() {
val coroutineScope = rememberCoroutineScope()
val offsetX = remember { Animatable(0f) }
val offsetY = remember { Animatable(0f) }
Card(
modifier = Modifier
.padding(all = 16.dp)
.fillMaxWidth()
.height(100.dp)
.offset {
IntOffset(
offsetX.value.toInt(),
offsetY.value.toInt()
)
}
.pointerInput(Unit) {
detectDragGestures(
onDragEnd = {
coroutineScope.launch {
offsetY.animateTo(
targetValue = 0f,
animationSpec = tween(
durationMillis = 3000,
delayMillis = 0
)
)
}
coroutineScope.launch {
offsetX.animateTo(
targetValue = 0f,
animationSpec = tween(
durationMillis = 1000,
delayMillis = 0
)
)
}
},
onDrag = { change, dragAmount ->
change.consume()
coroutineScope.launch {
offsetY.snapTo(offsetY.value + dragAmount.y)
}
coroutineScope.launch {
offsetX.snapTo(offsetX.value + dragAmount.x)
}
}
)
},
elevation = 4.dp,
shape = RoundedCornerShape(12.dp),
backgroundColor = Color.White
) {
Box(contentAlignment = Alignment.Center) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Rodrigo Martin Dominguez")
Text(text = "Android Engineer")
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment