如何为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
相关产品推荐
相关产品推荐

