Skip to content

探讨一下另外一种方式实现放大缩小步幅一致的可行性 #584

@johnnhan

Description

@johnnhan

问题描述

问题最早可追溯至 #347 ,当时我提出的问题大概是 “使用鼠标向下平移画布比向上平移快;shift+鼠标向右平移比向左平移快;ctrl+鼠标缩小比放大快”,没过多久又有其他人提出了类似 issue #383 。后来我在 #347 追问后官方给出了两个方案:

  1. 使用 custom 自定义窗口缩放逻辑
  2. 新增了 posDeltaSpeednegDeltaSpeed 两个配置项

不知道为什么我总感觉这不是最佳的解决方案

鼠标平移步幅不一致的完美解决

后来,有人通过查看源码,找到了通过鼠标平移步幅不一致的问题所在

Image

原因是只对正 delta 值做了 50 的限制

官方看到 issue 后也在第一时间更新发布,非常感谢🙏🙏🙏。

鼠标缩放步幅不一致原因猜想

有了前人的经验,我也想通过查看源码的方式看能不能找到鼠标缩放步幅不一致的问题所在。

我看看能不能先把这个异常现象说的更通俗一些:
参考miro,假如画布上一个元素,鼠标向下滚动一次缩小一次画布,再向上滚动一次放大一次画布,这个时候元素看起来和之前是一样大的(画布的缩放值一缩一放回到了之前大小)。

我试着找找原因

1.鼠标问题。我是window11笔记本+外置鼠标,而不是笔记本自带触控板。可能因为现在鼠标灵敏度比较高,向下滚动一次deltaY值为333,向上滚动一次deltaY值-333

Image

鼠标deltaY值太大,如果使用笔记本触控板,不会有这么大的deltaY。
(非根本原因,但会影响within)

2.代码限制。packages\viewport\src\interaction\WheelEventHelper.ts,getScale方法

getScale(event: IWheelEvent, config: IWheelConfig): number {

        let zoom: boolean
        let scale = 1
        let { zoomMode, zoomSpeed } = config

        const delta = event.deltaY || event.deltaX

        if (zoomMode) {
            // mac 触摸板滚动手势的deltaY是整数, 鼠标滚动/触摸板缩放的deltaY有小数点, firfox鼠标滚动为整数,为18或19的倍数
            // windows 始终是整数
            zoom = (zoomMode === 'mouse') ? true : (!event.deltaX && (Platform.intWheelDeltaY ? Math.abs(delta) > 17 : Math.ceil(delta) !== delta))
            if (event.shiftKey || event.metaKey || event.ctrlKey) zoom = true
        } else {
            zoom = !event.shiftKey && (event.metaKey || event.ctrlKey)
        }

        if (zoom) {
            zoomSpeed = within(zoomSpeed, 0, 1)
            const min = event.deltaY ? config.delta.y : config.delta.x
            scale = within(1 - delta / (min * 4) * zoomSpeed, 0.5, 1.5) // zoomSpeed
        }

        return scale
}

重点关注 if (zoom) {} 代码块。我们来举个例子,假如我的鼠标deltaY是300,配置项delta.y为100,zoomSpeed 为默认值0.5
鼠标滚动缩小一次:
scale = within(1 - 300/ (100 * 4) * 0.5, 0.5, 1.5) = 0.625
鼠标滚动放大一次:
scale = within(1 + 300/ (100 * 4) * 0.5, 0.5, 1.5) = 1.375
如此一缩一放,画布缩放值=1 * 0.625 * 1.375 = 0.859375,所以看起来元素变小了。

缩放的方案可行性探讨

我们假设画布初始缩放值为s,缩小的时候scale是a,放大的时候scale是b,我们希望sab=s,即a*b=1,这样就能实现一缩一放画布缩放值能回到之前,所以修改 if (zoom) {} 代码块如下:

if (zoom) {
    zoomSpeed = within(zoomSpeed, 0, 1)
    const min = event.deltaY ? config.delta.y : config.delta.x
    const pace = within(1 - Math.abs(delta) / (min * 4) * zoomSpeed, 0.5, 2) // 最大值限制改成了2
    scale = delta > 0 ? pace : 1 / pace
}

举个例子测试一下,假如我的鼠标deltaY是300,配置项delta.y为100,zoomSpeed 为默认值0.5
pace = within(1 - 300/ (100 * 4) * 0.5, 0.5, 2) = 0.625
鼠标滚动缩小一次:
scale = 0.625
鼠标滚动放大一次:
scale = 1 / 0.625 = 1.6
如此一缩一放,画布缩放值=1 * 0.625 * 1.6 = 1,画布缩放值回到之前。

不知道这个方案可不可行呢

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions