Home > other >  Jetpack Compose icon shadow/elevation
Jetpack Compose icon shadow/elevation

Time:07-12

Is there a way to have an Icon (with ImageVector) component with a shadow/elevation in Jetpack Compose?

I want to make an IconButton with an elevated Icon but there seems to be no solution available for this problem. Things like Modifier.shadow() will only draw a shadow box around my icon and the Icon component itself has no elevation parameter.

This ticket seems like a duplicate of icon with shadow

CodePudding user response:

What you require is a library that converts imageVectors or xml files into Path. As i know of there is no built-in library for this. There are probably few out there that converts into Path or Shape.

When you have a shape or path what you need to do is draw with this shape as Modifier or into Canvas

fun Modifier.vectorShadow(
    path: Path,
    x: Dp,
    y: Dp,
    radius: Dp
) = composed(
    inspectorInfo = {
        name = "vectorShadow"
        value = path
        value = x
        value = y
        value = radius
    },
    factory = {

        val paint = remember {
            Paint()
        }

        val frameworkPaint = remember {
            paint.asFrameworkPaint()
        }

        val color = Color.DarkGray
        val dx: Float
        val dy: Float
        val radiusInPx: Float

        with(LocalDensity.current) {
            dx = x.toPx()
            dy = y.toPx()
            radiusInPx = radius.toPx()
        }


        drawBehind {

            this.drawIntoCanvas {

                val transparent = color
                    .copy(alpha = 0f)
                    .toArgb()

                frameworkPaint.color = transparent

                frameworkPaint.setShadowLayer(
                    radiusInPx,
                    dx,
                    dy,
                    color
                        .copy(alpha = .7f)
                        .toArgb()
                )

                it.drawPath(path, paint)


            }
        }
    }
)

Usage

Column(
    modifier = Modifier
        .fillMaxSize()
        .padding(8.dp)
) {

    val center = with(LocalDensity.current) {
        150.dp.toPx()
    }
    val path1 = createPolygonPath(center, center, 6, center)
    val path2 = createPolygonPath(center, center, 5, center)

    Canvas(
        modifier = Modifier
            .size(300.dp)
            .vectorShadow(path1, 0.dp, 0.dp, 6.dp)
            .border(3.dp, Color.Green)
    ) {
        drawPath(path1, Color.White)
    }
    Spacer(modifier = Modifier.height(10.dp))
    Canvas(
        modifier = Modifier
            .size(300.dp)
            .vectorShadow(path2, 3.dp, 3.dp, 10.dp)
            .border(3.dp, Color.Green)
    ) {
        drawPath(path2, Color.White)
    }
}

Result

enter image description here

createPolygonPath is a sample function to create Path. If you manage to convert your vector to Path rest is simple.

fun createPolygonPath(cx: Float, cy: Float, sides: Int, radius: Float): Path {
    val angle = 2.0 * Math.PI / sides

    return Path().apply {
        moveTo(
            cx   (radius * cos(0.0)).toFloat(),
            cy   (radius * sin(0.0)).toFloat()
        )
        for (i in 1 until sides) {
            lineTo(
                cx   (radius * cos(angle * i)).toFloat(),
                cy   (radius * sin(angle * i)).toFloat()
            )
        }
        close()
    }
}

CodePudding user response:

It's not exactly what you want but for elevating an icon you can simply do this:

Icon(
    Icons.Outlined.Refresh, contentDescription = "back",
    modifier = Modifier
        .size(300.dp)
        .offset(10.dp, 10.dp), tint = Color(0, 0, 0, 40)
)
Icon(
    Icons.Outlined.Refresh, contentDescription = "front",
    modifier = Modifier.size(300.dp), tint = Color(0xFFb6d7a8)
)

enter image description here

The problem is that it is lacking the blurring effect.

  • Related