Created
January 6, 2024 15:07
-
-
Save ardakazanci/acfcd9144474deac7bc5fa3d0531f0a9 to your computer and use it in GitHub Desktop.
Heart Shape and Animation for Jetpack Compose
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Composable | |
fun AnimatedHeartShape() { | |
BoxWithConstraints( | |
modifier = Modifier.fillMaxSize(), | |
contentAlignment = Alignment.Center | |
) { | |
var clicked by remember { mutableStateOf(false) } | |
val heartSize = 400.dp | |
val heartSizePx = with(LocalDensity.current) { heartSize.toPx() } | |
val infiniteTransition = rememberInfiniteTransition(label = "Sample rememberInfiniteTransition") | |
val animatedStrokePhase = infiniteTransition.animateFloat( | |
initialValue = 0f, | |
targetValue = heartSizePx * 2, | |
animationSpec = infiniteRepeatable( | |
animation = tween(2000, easing = LinearEasing), | |
repeatMode = RepeatMode.Restart | |
), label = "Sample AnimatedFloat" | |
) | |
val continuousScale = infiniteTransition.animateFloat( | |
initialValue = 0.8f, | |
targetValue = 1f, | |
animationSpec = infiniteRepeatable( | |
animation = tween(500, easing = LinearEasing), | |
repeatMode = RepeatMode.Reverse | |
), label = "Sample AnimatedFloat" | |
) | |
val disappearScale by animateFloatAsState( | |
targetValue = if (clicked) 0f else 1f, | |
animationSpec = tween( | |
durationMillis = 500, | |
easing = LinearOutSlowInEasing | |
), label = "Sample Float As State" | |
) | |
Canvas( | |
modifier = Modifier | |
.size(heartSize) | |
.clickable { clicked = true } | |
) { | |
val path = Path().apply { | |
moveTo(heartSizePx / 2, heartSizePx / 5) | |
cubicTo(heartSizePx * 3 / 4, 0f, heartSizePx, heartSizePx / 3, heartSizePx / 2, heartSizePx) | |
cubicTo(0f, heartSizePx / 3, heartSizePx / 4, 0f, heartSizePx / 2, heartSizePx / 5) | |
close() | |
} | |
val gradient = Brush.linearGradient( | |
colors = listOf(Color.Red, Color(0xFF841C26), Color(0xFFBA274A)), | |
start = Offset(0f, 0f), | |
end = Offset(heartSizePx, heartSizePx) | |
) | |
val combinedScale = continuousScale.value * disappearScale | |
scale(combinedScale, combinedScale, pivot = Offset(heartSizePx / 2, heartSizePx / 2)) { | |
drawPath(path, gradient) | |
val lineGradient = Brush.linearGradient( | |
colors = listOf(Color(0xFFFFE45E), Color(0xFFFF6392)), | |
start = Offset(0f, 0f), | |
end = Offset(heartSizePx, heartSizePx) | |
) | |
val pathEffect = PathEffect.dashPathEffect( | |
intervals = floatArrayOf(heartSizePx, heartSizePx), | |
phase = -animatedStrokePhase.value | |
) | |
drawPath( | |
path = path, | |
brush = lineGradient, | |
style = Stroke(width = 8.dp.toPx(), pathEffect = pathEffect) | |
) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment