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

Jetpack Compose Desktop鼠标滚轮定点缩放实现问题

Kotlin Compose Multiplatform 鼠标滚轮定点缩放修复方案

你的问题核心在于坐标转换逻辑错误,以及缩放时偏移量的计算方式不符合定点缩放要求。要实现图形软件常见的“鼠标滚轮缩放时光标位置不变”效果,需保证缩放前后鼠标所在屏幕点对应的内容坐标(世界坐标)一致,基于此逻辑修正代码即可。

修正步骤及代码

  1. 修正坐标转换函数
    原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
    }
}
  1. 修正缩放时的偏移量计算
    替换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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 06:34:53