Mac 鼠标滚轮一格一格地跳

macOS 把鼠标滚轮当作一串离散的「格」交给应用,而触控板拿到的是连续手势。要让滚轮变顺滑,就得把这些格拦截下来、按帧重新投递——三个真实的坑就藏在这里。

macOS 拿到滚轮后到底做了什么

触控板上报的是连续手势——系统收到一串位置,并且知道手指还在动。鼠标滚轮上报的是离散的「格」:一格就是一个事件,附带一个行数,macOS 把它交给应用,然后就不管了。

所以滚轮「一格一格地跳」,不是哪里坏了,而是它本来就被这样要求:一格,一跳。会平滑滚轮的应用程序是自己做的;大多数不做。

真正有效的机制

三步,缺一不可:

这正是开源项目 MOS 普及开来的架构,也是正确的做法——我们的滚动引擎是照它实现的,而不是另发明一个更差的滚轮。

坑一——并不是每一格都意味着「滚动」

这一条最咬人。在「照片」「地图」「Keynote」「Numbers」以及大多数表格里,一格滚轮根本不是滚动,而是缩放的一步或翻一页。

一旦平滑化,这一格会在你松手很久之后还在到达:照片从 80% 跳到 200%,幻灯一次翻过十几页。用户于是用「按应用加白名单」来绕开——而这恰恰是这类功能本该替人省掉的手工活。

所以在改造一个事件之前,引擎先问一个问题:这是滚动还是缩放?缩放语义的事件原样放行,不做任何加工。对滚轮本来就是「按行精确」的应用同理——再加工只会更差,不会更好。

坑二——悄悄变成僵尸的显示时钟

CVDisplayLink 并不总能活过一夜。显示器唤醒之后,或者发生重配置(换线、过 KVM、改分辨率),它可能停止投递回调,却仍然声称自己在运行。

如果没人检查,外面看到的现象就是滚动突然不动画了——以及「这应用莫名其妙坏了」。修法并不漂亮:假定这个时钟会死,检测它,然后重建它。

坑三——两个工具抢同一个滚轮

同一层上的两个事件监听器都会消费滚轮。每个引擎都把对方吞掉的事件当成「输入丢了」,又把自己重新发出的事件当成「新输入」。两个插值器于是互相追,结果就是滚动卡顿、翻倍,或者干脆停摆。

用户很自然地得出结论:新装的那个工具搞坏了我的滚动。把冲突说出来(「检测到另一个滚动工具在运行」)远比让人盲目排查有用。

如果现在滚动就很难受,先查这几条

诚实的边界

平滑是改造输入,不是创造输入。它天然会带来一点延迟,也无法让一个本来就忽略精确增量的应用变好。当滚轮本来就已经正常时,正确答案是别去动它——这也正是上面那些语义与「按行精确」判断存在的原因。

相关

按时段调暗,最后把屏幕调成了全黑 · 外接显示器的亮度怎么调? · 鼠标加速关不掉 · 全部指南 · DDC/CI 无响应 · 休眠后亮度被重置 · 文字发虚 · 色彩发灰 · 该用哪个工具?