How can I animate a solid color over an Image?

43 Views Asked by At

I have this square image component with rounded corners that I want to overlay a solid color over on hover:

@Composable
fun MyImage(imageUri: String) {
    var likeOverlayColor = animateColorAsState(targetValue = Color(0, 0, 0, 100))
    val imageShape = RoundedCornerShape((WindowSize.windowHeight / 10).dp)
    val imageAspectRatio = 1f

    Box {
        Image(
            painter = painterResource(imageUri),
            contentDescription = null,
            contentScale = ContentScale.Crop,
            alignment = Alignment.TopStart,
            modifier = Modifier
                .fillMaxHeight()
                .aspectRatio(imageAspectRatio)
                .clip(imageShape)
                .align(Alignment.CenterStart)
        )
    }
}

How would I now overlay a solid color (rgb: 0, 0, 0, 100) over it on hover?

I have tried to use

var imageOverlayColor = remember { mutableStateOf(Color(0, 0, 0, 0)) }

Box (
    Modifier.onPointerEvent(
        eventType = PointerEventType.Enter,
        pass = PointerEventPass.Main,
        onEvent = {
            imageOverlayColor = animateColorAsState(targetValue = Color(0, 0, 0, 100))
        }
    )
) { 
    Image (...) {

    }

    Surface (
        modifier = Modifier
            .fillMaxHeight()
            .aspectRatio(imageAspectRatio)
            .clip(imageShape)
            .align(Alignment.CenterStart)
    ) {}
}

but got this error: @Composable invocations can only happen from the context of a @Composable function

1

There are 1 best solutions below

1
On BEST ANSWER

The problem is here that you are calling the @Composable function animateColorAsState inside the onEvent handler context, which is not a composable function.

To use animateColorAsState correctly, you should set your color outside of the event handler. Use for example a variable showOverlay to save the state of the overlay. Then you can specify different colors for your imageOverlayColor.

See the example code:

@Composable
fun MyImage(imageUri: String) {
    val imageShape = RoundedCornerShape((WindowSize.windowHeight / 10).dp)
    val imageAspectRatio = 1f

    // boolean variable for overlay visibility
    var showOverlay by remember { mutableStateOf(false) }
    // Specify different color depending on showOverlay value
    val imageOverlayColor = animateColorAsState(if (showOverlay) Color(0, 0, 0, 0) else Color(0, 0, 0, 100))

    Box (
        Modifier.onPointerEvent(
            eventType = PointerEventType.Enter,
            pass = PointerEventPass.Main,
            onEvent = {
                // toggle overlay visibility
                showOverlay = !showOverlay
            }
        )
    ) {
        Image (...) {

        }

        Surface (
            modifier = Modifier
                .fillMaxHeight()
                .aspectRatio(imageAspectRatio)
                .clip(imageShape)
                .align(Alignment.CenterStart)
        ) {}
    }
}

And if you just want to change the opacity of the element, consider using just a value overlayOpacity and use animateFloatAsState.