From 55c97cab7ef73381420c6c12712d3b78f7f45b74 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Thu, 23 Oct 2025 21:32:51 +0800 Subject: [PATCH] perf: improve smoothness of Arrow Up/Down scrolling, closes #2080 (#2306) --- .../src/app/reader/hooks/useBookShortcuts.ts | 28 +++++++++++++++++++ .../app/reader/utils/iframeEventHandlers.ts | 2 +- apps/readest-app/src/helpers/shortcuts.ts | 6 ++-- apps/readest-app/src/hooks/useShortcuts.ts | 11 ++++---- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts b/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts index 8ebbabcd..1c159995 100644 --- a/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts +++ b/apps/readest-app/src/app/reader/hooks/useBookShortcuts.ts @@ -81,6 +81,32 @@ const useBookShortcuts = ({ sideBarBookKey, bookKeys }: UseBookShortcutsProps) = getView(sideBarBookKey)?.next(distance); }; + const goPrevArrowUp = (event?: KeyboardEvent | MessageEvent) => { + const view = getView(sideBarBookKey); + if ( + view?.renderer.scrolled && + event instanceof MessageEvent && + event.data.type === 'iframe-keydown' + ) { + // already handled in the iframe for better smoothness + return; + } + view?.prev(distance); + }; + + const goNextArrowDown = (event?: KeyboardEvent | MessageEvent) => { + const view = getView(sideBarBookKey); + if ( + view?.renderer.scrolled && + event instanceof MessageEvent && + event.data.type === 'iframe-keydown' + ) { + // already handled in the iframe for better smoothness + return; + } + view?.next(distance); + }; + const goBack = () => { getView(sideBarBookKey)?.history.back(); }; @@ -194,6 +220,8 @@ const useBookShortcuts = ({ sideBarBookKey, bookKeys }: UseBookShortcutsProps) = onGoRight: goRight, onGoPrev: goPrev, onGoNext: goNext, + onGoPrevArrowUp: goPrevArrowUp, + onGoNextArrowDown: goNextArrowDown, onGoHalfPageDown: goHalfPageDown, onGoHalfPageUp: goHalfPageUp, onGoPrevSection: goPrevSection, diff --git a/apps/readest-app/src/app/reader/utils/iframeEventHandlers.ts b/apps/readest-app/src/app/reader/utils/iframeEventHandlers.ts index bc77d9b4..694a2409 100644 --- a/apps/readest-app/src/app/reader/utils/iframeEventHandlers.ts +++ b/apps/readest-app/src/app/reader/utils/iframeEventHandlers.ts @@ -5,7 +5,7 @@ let lastClickTime = 0; let longHoldTimeout: ReturnType | null = null; export const handleKeydown = (bookKey: string, event: KeyboardEvent) => { - if (['Backspace', 'ArrowDown', 'ArrowUp'].includes(event.key)) { + if (['Backspace'].includes(event.key)) { event.preventDefault(); } window.postMessage( diff --git a/apps/readest-app/src/helpers/shortcuts.ts b/apps/readest-app/src/helpers/shortcuts.ts index 35305b45..5a369f79 100644 --- a/apps/readest-app/src/helpers/shortcuts.ts +++ b/apps/readest-app/src/helpers/shortcuts.ts @@ -15,8 +15,10 @@ const DEFAULT_SHORTCUTS = { onQuitApp: ['ctrl+q', 'cmd+q'], onGoLeft: ['ArrowLeft', 'PageUp', 'h', 'shift+ '], onGoRight: ['ArrowRight', 'PageDown', 'l', ' '], - onGoNext: ['ArrowDown', 'j'], - onGoPrev: ['ArrowUp', 'k'], + onGoNext: ['j'], + onGoPrev: ['k'], + onGoNextArrowDown: ['ArrowDown'], + onGoPrevArrowUp: ['ArrowUp'], onGoLeftSection: ['opt+ArrowLeft', 'alt+ArrowLeft'], onGoRightSection: ['opt+ArrowRight', 'alt+ArrowRight'], onGoPrevSection: ['opt+ArrowUp', 'alt+ArrowUp'], diff --git a/apps/readest-app/src/hooks/useShortcuts.ts b/apps/readest-app/src/hooks/useShortcuts.ts index 0fe7fcce..a2e0828d 100644 --- a/apps/readest-app/src/hooks/useShortcuts.ts +++ b/apps/readest-app/src/hooks/useShortcuts.ts @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import { loadShortcuts, ShortcutConfig } from '../helpers/shortcuts'; export type KeyActionHandlers = { - [K in keyof ShortcutConfig]?: () => void; + [K in keyof ShortcutConfig]?: (event?: KeyboardEvent | MessageEvent) => void; }; const useShortcuts = (actions: KeyActionHandlers, dependencies: React.DependencyList = []) => { @@ -52,12 +52,13 @@ const useShortcuts = (actions: KeyActionHandlers, dependencies: React.Dependency altKey: boolean, metaKey: boolean, shiftKey: boolean, + event: KeyboardEvent | MessageEvent, ) => { // FIXME: This is a temporary fix to disable Back button navigation if (key === 'backspace') return true; for (const [actionName, actionHandler] of Object.entries(actions)) { const shortcutKey = actionName as keyof ShortcutConfig; - const handler = actionHandler as (() => void) | undefined; + const handler = actionHandler as ((event?: KeyboardEvent | MessageEvent) => void) | undefined; const shortcutList = shortcuts[shortcutKey as keyof ShortcutConfig]; if ( handler && @@ -65,7 +66,7 @@ const useShortcuts = (actions: KeyActionHandlers, dependencies: React.Dependency isShortcutMatch(shortcut, key, ctrlKey, altKey, metaKey, shiftKey), ) ) { - handler(); + handler(event); return true; } } @@ -94,7 +95,7 @@ const useShortcuts = (actions: KeyActionHandlers, dependencies: React.Dependency return; } - const handled = processKeyEvent(key.toLowerCase(), ctrlKey, altKey, metaKey, shiftKey); + const handled = processKeyEvent(key.toLowerCase(), ctrlKey, altKey, metaKey, shiftKey, event); if (handled) event.preventDefault(); } else if ( event instanceof MessageEvent && @@ -102,7 +103,7 @@ const useShortcuts = (actions: KeyActionHandlers, dependencies: React.Dependency event.data.type === 'iframe-keydown' ) { const { key, ctrlKey, altKey, metaKey, shiftKey } = event.data; - processKeyEvent(key.toLowerCase(), ctrlKey, altKey, metaKey, shiftKey); + processKeyEvent(key.toLowerCase(), ctrlKey, altKey, metaKey, shiftKey, event); } };