Repository navigation
鼠标滚轮选择时候的抖动--从discussion过来的 #463
lcfactorization
started this conversation in
General
Replies: 1 comment
|
先说结论:你说得对,这不是扩展的问题,是 CodeMirror 6 在拖选时的一个正反馈循环,而且可以用一段很轻的"护栏"代码消除。我在同一手势下做了开启/关闭的实测对比。 一、现象(和你的描述一致)在编辑器里按住鼠标向下拖选,当指针停在可视区底部附近时:
用 44 步标准手势复现,纯应用(不依赖任何扩展)下:
二、原因关键事实:每一次 scrollTop 写入都来自 CodeMirror 自己的 正反馈循环是这样的:
增益≈1,永不衰减,所以会以帧率一路跑飞,直到文档末尾;末尾那次 measure 把视图拽回锚点(−17287px),再重来。
三、修复用的关键护栏代码思路:不去跟 CM6 的 measure 较劲,只在拖选进行中、只对编辑器滚动容器 // 在编辑器页面里,拖选进行中给 scrollTop 写入加一道护栏
(function () {
'use strict';
if (window.__zcDragGuardInstalled) return;
window.__zcDragGuardInstalled = 1;
var S = window.__zcDragGuard = {
on: true, budget: 24, freezeInside: true,
active: false, dir: 0, lastY: 0, ptrY: 0,
rejected: 0, clamped: 0, frozen: 0, passed: 0, wheelUntil: 0
};
var bucket = 0, used = 0;
var d = Object.getOwnPropertyDescriptor(Element.prototype, 'scrollTop');
if (!d || !d.set || !d.get) return;
function now() {
return (window.performance && performance.now) ? performance.now() : Date.now();
}
Object.defineProperty(Element.prototype, 'scrollTop', {
configurable: true, enumerable: d.enumerable, get: d.get,
set: function (x) {
// 非拖选 / 非编辑器容器 / 滚轮旁路:原样放行
if (!S.on || !S.active || !this.classList || !this.classList.contains('cm-scroller'))
return d.set.call(this, x);
if (now() < S.wheelUntil) { S.passed++; return d.set.call(this, x); }
var before = d.get.call(this);
var delta = x - before;
if (!delta) return;
// 1) 反向滚动直接丢弃(消灭跑到末尾后的 -17287 回弹)
if (S.dir > 0 && delta < -4) { S.rejected++; return; }
if (S.dir < 0 && delta > 4) { S.rejected++; return; }
// 2) 指针已在视口内(离上下边 >24px)时完全冻结:
// 这时 head 就在指针下,没有要揭示的内容,这里滚动只会让文字从指针下滑走
if (S.freezeInside) {
var r = this.getBoundingClientRect();
var band = 24;
if (S.ptrY >= r.top + band && S.ptrY <= r.bottom - band) { S.frozen++; return; }
}
// 3) 每帧预算:其余滚动按 24px/帧(≈1400px/s,正常自动滚速度)截断
var t = now();
if (t - bucket > 16) { bucket = t; used = 0; }
var room = S.budget - used;
if (room < 0) room = 0;
var mag = delta < 0 ? -delta : delta;
if (mag > room) { mag = room; S.clamped++; }
if (!mag) return;
used += mag;
d.set.call(this, delta < 0 ? before - mag : before + mag);
}
});
// 拖选开始:记录方向、指针位置;松手/失焦:解除
document.addEventListener('pointerdown', function (e) {
var t = e.target;
if (!t || !t.closest || !t.closest('.cm-content')) return;
S.active = true; S.dir = 0; S.lastY = S.ptrY = e.clientY;
bucket = 0; used = 0;
}, true);
document.addEventListener('pointermove', function (e) {
if (!S.active) return;
if (e.buttons === 0) { S.active = false; return; }
S.ptrY = e.clientY;
if (e.clientY > S.lastY + 1) S.dir = 1;
else if (e.clientY < S.lastY - 1) S.dir = -1;
S.lastY = e.clientY;
}, true);
function release() { S.active = false; }
document.addEventListener('pointerup', release, true);
document.addEventListener('pointercancel', release, true);
window.addEventListener('blur', release, true);
// 拖选中滚轮永远可用(这才是延长选区的正道),180ms 内不受护栏约束
document.addEventListener('wheel', function () { S.wheelUntil = now() + 180; },
{ capture: true, passive: true });
})();(上面省略了可配置的 localStorage 读取部分,需要的话可以加一个开关项。) 实测同一 44 步手势,开启护栏后:
四、可行性与取舍(供评估)
如果方便,我可以把护栏封装成一个可开关的偏好,或者直接给一版基于 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
#437
看来不止一个人发现这个问题:鼠标滚轮选择的时候,会发生抖动,有时候比较严重,向下选择却会上跳并选择高亮,有时候是向下滚动一点,却直接选择到页面最下方。
我原来以为是我用了扩展的问题,后来发现不是,再从discussion看到有一位网友碰到类似问题,才明白可能是程序问题
All reactions