So I need to implement similar shape in Jetpack Compose
I'm trying it with the following code but for now with no luck:
fun clippedCardShape(clipRadius: Float = 30f): Shape {
return object : Shape {
override fun createOutline(
size: androidx.compose.ui.geometry.Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
val path = Path().apply {
// Top line
lineTo(size.width, 0f)
// Right side clipping
lineTo(size.width, size.height / 2 - clipRadius)
arcTo(
rect = Rect(
left = size.width - clipRadius * 2,
top = size.height / 2 - clipRadius,
right = size.width,
bottom = size.height / 2 + clipRadius
),
startAngleDegrees = 270f,
sweepAngleDegrees = -180f,
forceMoveTo = false
)
lineTo(size.width, size.height)
// Bottom line
lineTo(0f, size.height)
// Left side clipping
lineTo(0f, size.height / 2 + clipRadius)
arcTo(
rect = Rect(
left = 0f,
top = size.height / 2 - clipRadius,
right = clipRadius * 2,
bottom = size.height / 2 + clipRadius
),
startAngleDegrees = 90f,
sweepAngleDegrees = 180f,
forceMoveTo = false
)
lineTo(0f, 0f)
close()
}
return Outline.Generic(path)
}
}
}
The code tries to add clipping at the middle, but it should be almost at the top of the shape like on the example image.
Result:
So I would like to see a working example from someone who knows how to deal with it so I could learn it and figure out the logic and math how implement to similar shapes for different cases.
Update
a little progress but still with issues:
val clipRadius = 20f // Radius of the left and right clips
val path = Path().apply {
// Move to the top-left corner
moveTo(0f, 0f)
// Top-left corner
lineTo(0f, 0f)
// Left clip arc
arcTo(
rect = Rect(
left = 0f,
top = (size.height / 2) - clipRadius,
right = clipRadius * 2,
bottom = (size.height / 2) + clipRadius
),
startAngleDegrees = 270f,
sweepAngleDegrees = 180f,
forceMoveTo = false
)
// Bottom-left corner
lineTo(0f, size.height)
// Bottom-left to bottom-right
lineTo(size.width, size.height)
// Right clip arc
arcTo(
rect = Rect(
left = size.width - (clipRadius * 2),
top = (size.height / 2) - clipRadius,
right = size.width,
bottom = (size.height / 2) + clipRadius
),
startAngleDegrees = 90f,
sweepAngleDegrees = 180f,
forceMoveTo = false
)
// Bottom-right corner
lineTo(size.width, 0f)
// Close the shape
close()
}
return Outline.Generic(path)
You can do it like this, using addOval
:
@Composable
fun ClippedCardShape() {
Scaffold { paddingValues ->
LazyColumn(Modifier.padding(paddingValues)) {
items(10) {
Image(
modifier = Modifier
.fillMaxWidth()
.clip(clippedCardShape()),
painter = painterResource(R.drawable.ic_launcher_background),
contentDescription = null,
contentScale = ContentScale.FillBounds
)
}
}
}
}
fun clippedCardShape(clipRadius: Float = 30f, clipPosition: Float = 0.2f): Shape =
object : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
val fullPath = Path().apply {
addRect(
Rect(
left = 0f,
top = 0f,
right = size.width,
bottom = size.height
)
)
}
val path = Path().apply {
addOval(
oval = Rect(
center = Offset(x = 0f, y = size.height * clipPosition),
radius = clipRadius
),
)
addOval(
oval = Rect(
center = Offset(x = size.width, y = size.height * clipPosition),
radius = clipRadius
),
)
}
return Outline.Generic(fullPath - path)
}
}