Ribbon Hover
Code
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import theme.Lime400
import theme.Pink400
import theme.Sky400
import theme.Zinc100
import theme.Zinc300
import theme.Zinc50
import theme.Zinc950
import util.ribbon
@Composable
fun RibbonHover() {
Box(
modifier = Modifier
.fillMaxSize()
.background(
color = Zinc100
),
contentAlignment = Alignment.Center
) {
val interaction = remember { MutableInteractionSource() }
val isHovered by interaction.collectIsHoveredAsState()
val progress by animateFloatAsState(
targetValue = if (isHovered) 1f else 0f,
animationSpec = spring(
stiffness = Spring.StiffnessVeryLow,
),
visibilityThreshold = .00001f
)
val width by animateFloatAsState(
targetValue = if (progress > .2f) 1f else 0f,
animationSpec = spring(stiffness = Spring.StiffnessMediumLow)
)
Box(
modifier = Modifier
.hoverable(interaction)
.pointerHoverIcon(PointerIcon.Hand)
.ribbon(
brush = Brush.horizontalGradient(
colors = listOf(
Pink400,
Sky400,
Lime400,
)
),
progress = progress,
stroke = 10.dp * width,
loops = 3,
gap = 10.dp,
)
.background(
color = Zinc950,
shape = RoundedCornerShape(24.dp)
)
.border(
width = 2.dp,
brush = Brush.verticalGradient(
colors = listOf(
Zinc50.copy(alpha = .7f),
Zinc50.copy(alpha = .05f),
)
),
shape = RoundedCornerShape(24.dp)
)
.dropShadow(
shape = RoundedCornerShape(24.dp)
) {
radius = 10f
this.alpha = .3f
}
.padding(
horizontal = 32.dp, vertical = 16.dp
)
) {
Text(
"Ribbon Hover",
color = Zinc300,
fontSize = 32.sp,
fontWeight = FontWeight.SemiBold
)
}
}
}Wrapping a Fancy Ribbon around any Composable
How to animate a ribbon that wraps around UI elements in Jetpack Compose

