Skip to content

Commit edfc646

Browse files
committed
fix: 避免高精度鼠标滚轮滚动翻页过快
1 parent 2ed4a2b commit edfc646

1 file changed

Lines changed: 60 additions & 4 deletions

File tree

src/components/Manga/actions/operate.ts

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,21 +323,64 @@ const isMultipleOf = (a: number, b: number) => {
323323
return !decimal || decimal.startsWith('0000') || decimal.startsWith('9999');
324324
};
325325

326+
const normalizeWheelDeltaY = (e: WheelEvent) => {
327+
switch (e.deltaMode) {
328+
// 统一换算为近似像素距离,便于累计滚动量
329+
case WheelEvent.DOM_DELTA_LINE:
330+
return e.deltaY * 16;
331+
case WheelEvent.DOM_DELTA_PAGE:
332+
return e.deltaY * window.innerHeight;
333+
default:
334+
return e.deltaY;
335+
}
336+
};
337+
326338
let lastDeltaY = -1;
327339
let timeoutId = 0;
328340
let lastPageNum = -1;
329-
let wheelType: undefined | 'trackpad' | 'mouse';
341+
let wheelType: undefined | 'trackpad' | 'mouse' | 'highPrecisionWheel';
330342
let equalNum = 0;
331343
let diffNum = 0;
344+
let highPrecisionWheelDeltaY = 0;
345+
let highPrecisionWheelResetId = 0;
346+
347+
const highPrecisionWheelPageThreshold = 80;
348+
const highPrecisionWheelResetDelay = 100;
349+
350+
// 高精度滚轮按累计位移翻页,避免一次滚动触发多个 wheel 事件导致连翻
351+
const handleHighPrecisionWheel = (deltaY: number, dir: 'next' | 'prev') => {
352+
if (highPrecisionWheelResetId) clearTimeout(highPrecisionWheelResetId);
353+
highPrecisionWheelResetId = window.setTimeout(() => {
354+
highPrecisionWheelDeltaY = 0;
355+
}, highPrecisionWheelResetDelay);
356+
357+
if (highPrecisionWheelDeltaY * deltaY < 0) highPrecisionWheelDeltaY = 0;
358+
highPrecisionWheelDeltaY += deltaY;
359+
360+
if (Math.abs(highPrecisionWheelDeltaY) < highPrecisionWheelPageThreshold)
361+
return;
362+
363+
// 翻页后清空余量,避免单次滚动的尾部事件连翻
364+
highPrecisionWheelDeltaY = 0;
365+
if (!turnPage(dir)) return false;
366+
openScrollLock();
367+
return true;
368+
};
332369

333370
export const handleWheel = (e: WheelEvent) => {
334371
if (store.gridMode) return;
335372
e.stopPropagation();
336373
if (e.ctrlKey || e.altKey) e.preventDefault();
337374

338-
const isWheelDown = e.deltaY > 0;
375+
const normalizedDeltaY = normalizeWheelDeltaY(e);
376+
const isWheelDown = normalizedDeltaY > 0;
339377
const dir = isWheelDown ? 'next' : 'prev';
340378
const absDeltaY = Math.abs(e.deltaY);
379+
const isHighPrecisionWheelEvent =
380+
e.deltaMode === WheelEvent.DOM_DELTA_PIXEL &&
381+
Number.isInteger(e.deltaY) &&
382+
absDeltaY >= 5 &&
383+
absDeltaY < 100;
341384

342385
// 通过`两次滚动距离是否成倍数`和`滚动距离是否过小`来判断是否是触摸板
343386
if (
@@ -377,15 +420,15 @@ export const handleWheel = (e: WheelEvent) => {
377420
// 并排卷轴模式下
378421
if (isAbreastMode() && store.option.zoom.ratio === 100) {
379422
e.preventDefault();
380-
scrollBy(e.deltaY, true);
423+
scrollBy(normalizedDeltaY, true);
381424
}
382425

383426
// 防止滚动到网页
384427
if (!isScrollMode()) e.preventDefault();
385428

386429
// 为了避免因临时卡顿而误判为触摸板
387430
// 在连续几次滚动量均相同的情况下,将 wheelType 相关变量重置回初始状态
388-
if (diffNum < 10) {
431+
if (diffNum < 10 && wheelType !== 'highPrecisionWheel') {
389432
if (lastDeltaY === absDeltaY && absDeltaY > 5) equalNum += 1;
390433
else {
391434
diffNum += 1;
@@ -406,16 +449,29 @@ export const handleWheel = (e: WheelEvent) => {
406449
// 第一次触发滚动没法判断类型,就当作滚轮来处理
407450
// 但为了避免触摸板前两次滚动事件间隔大于帧生成时间导致得重新翻页回去的闪烁,加个延迟等待下
408451
lastPageNum = store.activePageIndex;
452+
highPrecisionWheelDeltaY = normalizedDeltaY;
409453
timeoutId = window.setTimeout(turnPage, 16, dir);
410454
return;
411455
}
456+
457+
// 高精度滚轮通常是像素单位的小段滚动,按累计位移处理
458+
if (isHighPrecisionWheelEvent) {
459+
if (timeoutId) clearTimeout(timeoutId);
460+
if (lastPageNum !== -1) setState('activePageIndex', lastPageNum);
461+
wheelType = 'highPrecisionWheel';
462+
return handleHighPrecisionWheel(normalizedDeltaY, dir);
463+
}
464+
412465
wheelType = 'mouse';
413466
}
414467
// falls through
415468

416469
case 'mouse':
417470
return turnPage(dir);
418471

472+
case 'highPrecisionWheel':
473+
return handleHighPrecisionWheel(normalizedDeltaY, dir);
474+
419475
case 'trackpad':
420476
return handleTrackpadWheel(e);
421477
}

0 commit comments

Comments
 (0)