触控板的自然滚动与网页视频的音量调整——以哔哩哔哩为例

·6min·技术·#UserScript#JavaScript

在触控板操作中,滚动方向遵循「自然滚动」原则:当用户向上滑动时,意味着展示下方内容,这种交互逻辑与现实世界的物理运动保持一致。相比之下,传统鼠标的滚动方向则采用相反的逻辑:向上滚动表示返回上方内容。这种差异导致同一操作方向在不同设备上产生截然不同的结果。然而,在音量调节的时候二者的语义则得到了统一:「向上就是升高,向下就是降低」。

* macOS 默认情况下鼠标和触控板的「自然滚动」是统一设置的,但是对于许多非 Magic Mouse 用户仍会使用诸如 Scroll Reserver/鼠标自带驱动 之类的第三方工具来实现分别设置。事实上这样才符合正常鼠标的逻辑,而 Magic Mouse 本质上仍是一个触控板。

问题缘由

最近使用笔记本比较多,MacBook 触控板还是很舒服的,特别是对于一些精细的场景,例如,当我需要将音量从 1% 精准调整到 4% 时,触控板的灵敏度非常合适。然而,问题也随之而来:触控板的滚动方向与音量调整的方向相反,这让我感到困惑。

研究思路

要想研究鼠标和触控板的区别,首先就需要知道他们在事件中的表现。然后要判断是否为触控板,再对其进行不同的处理。但是很可惜的是,现阶段浏览器并没有直接提供可以区分触控板和鼠标的 API。因此,我们只能通过一些 trick 来判断。

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(也可为其他数值,经测试这个数值相对合理)。

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 处理避免这种情况。目前测试设备有限,未来可能会有所变动。