一般情况下,触控板的滚动语义是这样的:向上滚动时候,其意味着「展示下面的信息」。由于这种操作和现实的逻辑相同,也被称为自然滚动。而鼠标则是向上滚动则意味着「回到上方的信息」*。因此同一个方向的操作下,就得到了不同的结果。然而,在音量调节的时候二者的语义则得到了统一:「向上就是升高,向下就是降低」。

* macOS 默认情况下鼠标和触控板的「自然滚动」是统一设置的,但是对于许多非 Magic Mouse 用户仍会使用诸如 Scroll Reserver/鼠标自带驱动 之类的第三方工具来实现分别设置。

问题缘由

最近使用笔记本比较多,MacBook 触控板还是很舒服的,特别是对于一些精细的场景,例如我只想精准的调节 1% ~ 4% 的音量的时候。这个时候使用触控板就遇到了上述场景。

研究思路

要想研究鼠标和触控板的区别,首先就需要知道他们在事件中的表现。然后要判断是否为触控板,再对其进行不同的处理。不过由于目前浏览器的 API 所限,目前仍然没有可以完美区别触控板和鼠标的方式。

Wheel Event

Wheel 是这样一个事件,他会接收鼠标或类似输入设备的参数,他分别会返回如下参数,(来自 MDN,有省略,b 站实际使用的是 mousewheel event 但是由于该标准已经不推荐使用,所以这里会使用 Wheel)

参数 说明
WheelEvent.deltaX 返回 double 值,该值表示滚轮的横向滚动量。
WheelEvent.deltaY 返回 double 值,该值表示滚轮的纵向滚动量。
WheelEvent.deltaZ 返回 double 值,该值表示滚轮的 z 轴方向上的滚动量。

因此,我写了如下代码方便测试差异。

See the Pen wheel event test by Max C. Foo (@maxchang3) on CodePen.

经过测试,结果如下:

⚠️ 由于硬件、浏览器差异,可能会有完全不同的表现,未作大范围测试,如有问题,欢迎反馈!

目前的逻辑是判断出鼠标,反过来得知触控板,鼠标测试硬件均为 Logitech MX Anywhere2s。

macOS [1] [2]

1.鼠标滚轮推动下的 deltaY 存在一个最小整数值,除去这个值之外的其他值大概率为浮点数,并且小数点后较为复杂。形如:235.867919921875

2.触控板大部分情况下为整数,存在为浮点数的触控板,但是一般也不会很复杂。形如:2.5

Windows [3]

1.未开启平滑滚动的情况下。通过不同的力度,仅能得到几个整数值。最低为 100。高至 600.

2.开启平滑滚动的情况下,数值为分布在 1 左右的浮点数。

结论

对于 macOS (除 Firefox),可以精准的区分触控板,采取人工矫正的方式,使用鼠标的最低刻度推动获取一个最低的整数 deltaY。除去这个值外的所有数值,都根据是否为整数[4]进行判断,是则为触控板,否则为鼠标。目前经过一段时间测试,效果良好。

对于 Windows 和任意平台的 Firefox。则需要设定一个合理的阈值,会出现判断出错的情况,但是对于音量精细调节的场景,是够用的。

以下是一个简单示,其中 MOUSE_MIN 为鼠标推动下的 deltaY 的最小整数值,-1 为直接取一个固定值 100(也可为其他数值,经测试这个数值相对合理)。

1
2
3
4
5
const isTrackpad = (wheelEvent: WheelEvent) => {
if (MOUSE_MIN === -1) return Math.abs(wheelEvent.deltaY) < 100
return Math.abs(wheelEvent.deltaY) != MOUSE_MIN &&
Number.isInteger(wheelEvent.deltaY * 2)
}

最终成果

我通过Hook EventTarget.prototype.addEventListener 拦截对应的 mousewheel 事件。(所以 b 站为什么不用 wheel!)

判断是否为触控板,添加代理拦截 wheelDelta 值,取相反数(这里直接取 deltaY 后做一定计算处理,他与 wheelDelta 正负相异)后返回。

目前已经写成了一个油猴脚本,使用 vite-plugin-monkey 进行工程化开发!完整项目请看这里~

安装方式在下面~

[Greasyfork] [Github Release] [Github Pages]

一点展望

某日想到一个有意思的设计,因为查到 macOS 有触控板的相关事件,不知道 Windows 也有没有。如果都有的话,理论上可以通过开启一个服务的方式把实时的鼠标 / 触控板发送至网页然后获取准确的信息……似乎代价有点大。

希望有相关的标准早点进浏览器吧(也许我以后会去提一个 RFC?)

注释

  • [1] 仅 Chrome / Safari,Firefox 下全部为整数值。
  • [2] 仅有限测试于 macOS 13.2.1(MacBook Air M1)
  • [3] 仅有限测试于 Windows 11(LEGION R7000)
  • [4] 由于 2 的原因,对 `deltaY` 做乘 2 处理避免这种情况。目前测试设备有限,未来可能会有所变动。