You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Jetpack Compose Canvas中绘制的矩形添加涟漪效果?

问题:Canvas绘制的矩形触摸时如何显示涟漪效果?

我有一个示例composable函数,其中包含在Canvas composable中绘制的rectangle。通过pointerInput Modifier中的detectTapGestures来检测Canvas内的触摸操作,尤其是rectangle坐标范围内的触摸。

@Composable
fun Sample() {
    var rectangleCoordinates by remember{ mutableStateOf(Rect.Zero) }
    Canvas(
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectTapGestures {
                    if (rectangleCoordinates.contains(it)) {
                        Log.d(topBarTag, "rectangle touched")
                    }
                }
            }

    ){
        val rectSize = Size(40.dp.toPx(), 40.dp.toPx())
        rectangleCoordinates = Rect(center, rectSize)
        drawRect(
            topLeft = center,
            size = rectSize,
            color = Color.Blue
        )
    }
}

请问是否有方法在触摸该rectangle时显示ripple效果?


解决方案

方法一:用Box叠加可点击组件(推荐)

直接在Canvas上方对应矩形的位置放置一个Box,借助Modifier.clickable自带的涟漪效果实现,逻辑简洁且符合Compose设计习惯:

@Composable
fun SampleWithRipple() {
    val rectSize = 40.dp
    // 提前计算矩形的左上角偏移量,避免在Canvas内重复计算
    val centerOffset = with(LocalDensity.current) {
        val screenWidth = LocalConfiguration.current.screenWidthDp.dp.toPx()
        val screenHeight = LocalConfiguration.current.screenHeightDp.dp.toPx()
        val pxRectSize = rectSize.toPx()
        Offset((screenWidth - pxRectSize) / 2, (screenHeight - pxRectSize) / 2)
    }

    Box(modifier = Modifier.fillMaxSize()) {
        // 保留原Canvas绘制逻辑
        Canvas(modifier = Modifier.fillMaxSize()) {
            drawRect(
                topLeft = centerOffset,
                size = Size(rectSize.toPx(), rectSize.toPx()),
                color = Color.Blue
            )
        }

        // 在矩形位置放置可点击的Box,触发涟漪
        Box(
            modifier = Modifier
                .size(rectSize)
                .offset { IntOffset(centerOffset.x.toInt(), centerOffset.y.toInt()) }
                .clickable(
                    interactionSource = remember { MutableInteractionSource() },
                    indication = rememberRipple()
                ) {
                    Log.d(topBarTag, "rectangle touched")
                }
        )
    }
}

方法二:自定义绘制涟漪效果

如果必须在Canvas内部处理,可以手动跟踪触摸状态,通过动画绘制涟漪:

@Composable
fun SampleCustomRipple() {
    var rectangleCoordinates by remember { mutableStateOf(Rect.Zero) }
    var rippleCenter by remember { mutableStateOf(Offset.Unspecified) }
    var rippleAlpha by remember { mutableStateOf(0f) }
    val rippleAnim = remember { Animatable(0f) }

    // 涟漪动画逻辑
    LaunchedEffect(rippleCenter) {
        if (rippleCenter != Offset.Unspecified) {
            rippleAlpha = 0.3f
            // 播放涟漪扩散动画
            rippleAnim.animateTo(1f, animationSpec = tween(500))
            // 动画结束后隐藏涟漪
            rippleAlpha = 0f
            rippleCenter = Offset.Unspecified
            rippleAnim.snapTo(0f)
        }
    }

    Canvas(
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectTapGestures { tapOffset ->
                    if (rectangleCoordinates.contains(tapOffset)) {
                        Log.d(topBarTag, "rectangle touched")
                        rippleCenter = tapOffset
                    }
                }
            }
    ) {
        val rectSize = Size(40.dp.toPx(), 40.dp.toPx())
        rectangleCoordinates = Rect(center, rectSize)
        // 绘制原矩形
        drawRect(
            topLeft = center,
            size = rectSize,
            color = Color.Blue
        )
        // 绘制涟漪
        if (rippleCenter != Offset.Unspecified) {
            drawCircle(
                color = Color.White.copy(alpha = rippleAlpha),
                center = rippleCenter,
                radius = rippleAnim.value * rectSize.width,
                blendMode = BlendMode.SrcOver
            )
        }
    }
}

内容的提问来源于stack exchange,提问作者Kun.tito

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:39:30