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.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.draw.innerShadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color.Companion.Transparent
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import theme.Colors
import theme.Colors.Zinc100
import theme.Colors.Zinc300
@Composable
fun BubbleButtonImpl(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxSize()
.background(Colors.Pink400),
contentAlignment = Alignment.Center
) {
BubbleButton(
onClick = {
}
) {
Text(
"Bubble Button",
color = Color.White
)
}
}
}
@Composable
fun BubbleButton(
modifier: Modifier = Modifier,
onClick: () -> Unit,
shape: Shape = RoundedCornerShape(16.dp),
content: @Composable RowScope.() -> Unit,
) {
var isPressed by remember { mutableStateOf(false) }
val scale by animateFloatAsState(
targetValue = if (isPressed) .85f else 1f,
animationSpec = if (isPressed)
spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMediumLow
)
else
spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioHighBouncy,
),
visibilityThreshold = .000001f
)
Row(
modifier = modifier
.pointerInput(Unit) {
detectTapGestures(
onPress = {
isPressed = true
tryAwaitRelease()
isPressed = false
},
onTap = {
onClick()
}
)
}
.pointerHoverIcon(PointerIcon.Hand)
.graphicsLayer {
scaleY = scale
scaleX = (1f - scale) + 1f
}
.dropShadow(
shape = shape
) {
radius = 80f
alpha = .2f
}
.border(
width = 0.dp,
shape = shape,
brush = Brush.verticalGradient(
colors = listOf(
Color.White,
Zinc300,
)
),
)
.innerShadow(
shape = shape
) {
radius = 40f
brush = Brush.verticalGradient(
colors = listOf(
Zinc100,
Zinc100,
)
)
alpha = .4f
}
.innerShadow(
shape = shape
) {
radius = 30f
spread = 1f
brush = Brush.verticalGradient(
colors = listOf(
Color.White,
Transparent,
Transparent,
)
)
alpha = .8f
}
.innerShadow(
shape = shape
) {
radius = 80f
spread = 1f
brush = Brush.verticalGradient(
colors = listOf(
Color.White,
Transparent,
Transparent,
)
)
alpha = .5f
}
.innerShadow(
shape = shape
) {
radius = 40f
spread = 1f
brush = Brush.linearGradient(
colors = listOf(
Colors.Transparent,
Colors.Rose500,
Colors.Violet500,
)
)
alpha = .5f
}
.padding(horizontal = 32.dp, vertical = 16.dp)
) {
content()
}
}