Jetpack Compose Desktop鼠标滚轮定点缩放实现问题
Kotlin Compose Multiplatform 鼠标滚轮定点缩放修复方案
你的问题核心在于坐标转换逻辑错误,以及缩放时偏移量的计算方式不符合定点缩放要求。要实现图形软件常见的“鼠标滚轮缩放时光标位置不变”效果,需保证缩放前后鼠标所在屏幕点对应的内容坐标(世界坐标)一致,基于此逻辑修正代码即可。
修正步骤及代码
- 修正坐标转换函数
原screenToWorld函数的转换逻辑完全错误,替换为正确的屏幕/世界坐标互转逻辑:
class PanAndZoomState { var size: IntSize = IntSize.Zero var offset by mutableStateOf(Offset.Zero) var scale by mutableStateOf(1f) fun scale(delta: Int) { scale = (scale * exp(delta * 0.2f)).coerceIn(0.25f, 1.75f) } // 屏幕坐标(相对于容器左上角)转世界坐标(内容原始坐标) fun screenToWorld(screenPos: Offset): Offset { return (screenPos - offset) / scale } // 世界坐标转屏幕坐标 fun worldToScreen(worldPos: Offset): Offset { return worldPos * scale + offset } }
- 修正缩放时的偏移量计算
替换onPointerEvent中的缩放逻辑,确保鼠标所在点的世界坐标在缩放前后保持不变:
@OptIn(ExperimentalComposeUiApi::class) @Composable fun PanAndZoom( modifier: Modifier = Modifier, state: PanAndZoomState = remember { PanAndZoomState() }, content: @Composable BoxScope.() -> Unit ) { Box( modifier = modifier .onPointerEvent(PointerEventType.Scroll) { val change = it.changes.first() val delta = change.scrollDelta.y.toInt().sign val screenPos = change.position // 记录缩放前鼠标对应的世界坐标 val targetWorldPos = state.screenToWorld(screenPos) // 执行缩放 state.scale(delta) // 计算新偏移量:让鼠标屏幕点仍对应原世界坐标 state.offset = screenPos - targetWorldPos * state.scale } .pointerInput(Unit) { // 平移逻辑保持不变 detectDragGestures { _, dragAmount -> state.offset += dragAmount } } .onGloballyPositioned { state.size = it.size } ) { Box( modifier = Modifier .matchParentSize() .graphicsLayer { scaleX = state.scale scaleY = state.scale translationX = state.offset.x translationY = state.offset.y }, contentAlignment = Alignment.Center, content = content ) } }
逻辑说明
- 定点缩放核心:缩放后,鼠标所在的屏幕点必须对应同一个内容坐标。
- 计算流程:先获取缩放前鼠标对应的内容坐标,缩放后重新计算偏移量,确保该内容坐标映射回原屏幕点。
- 平移逻辑无需修改:
dragAmount是屏幕坐标偏移,直接累加到offset即可正确对应内容平移。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

