Universal Auto Scroll

Press Alt + X to start auto-scroll or increase speed. X to pause/resume, Space / Esc to stop. Use Up/Down to adjust speed. Page Up/Down work normally during scroll/pause. Mouse Up/Down pauses during scroll and work normally during pause.

目前為 2025-03-11 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name           Universal Auto Scroll
// @version        2.0
// @namespace      MedX-AA
// @author         MedX
// @license        MIT
// @description    Press Alt + X to start auto-scroll or increase speed. X to pause/resume, Space / Esc to stop. Use Up/Down to adjust speed. Page Up/Down work normally during scroll/pause. Mouse Up/Down pauses during scroll and work normally during pause.
// @icon           data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABGUlEQVR4nO3VsS4EURQG4E9oRCEh0ZDQi3gDjV7lHXTeAYVo9gl0ahregEapoKEh0SFRbVbIyiSjEYnd2TtzZ5PzJSeZYjbzn/2LQwghlSN8oP/PFO8caqHeAOF/pni3dfpDTuvEArlFA7lFA7lFA7lFA7lFA7lFA7lFA7lFA7m1uoE9dCuErDrd8pvJvDYYvl/OS8oFOhkW6KRcYBKnDYY/x5TEpnHZQPhrzKjJLG5qDH+LOTVbxGMN4Z+xrCGreEsY/h3rGraR6Db0sCmTLXyOEP4L2zLbGWGBXS1xUCH8vhaZwPEQ4U/K37RKca3PBgh/UceVTXmtr3Jd2VTmcfdH+HssGBNLePp1ZVeMmTU8lP988RxCkN43yek7CExEuggAAAAASUVORK5CYII=
// @grant          none
// @include        *
// ==/UserScript==

// === CONFIGURATION ===
const INITIAL_SPEED = 30; // Pixels per second
const SPEED_MULTIPLIER = 2.0; // Speed increase factor
const SLOWDOWN_MULTIPLIER = 0.5; // Speed decrease factor

// === KEY BINDINGS ===
const START_KEY = 88; // Alt + X starts/increases speed
const PAUSE_KEY = 88; // X pauses/resumes (only while scrolling)
const STOP_KEYS = new Set([27, 32]); // Escape & Space both fully stop scrolling

const SCROLL_DOWN_KEYS = new Set([40]); // Down Arrow (Speeds up)
const SCROLL_UP_KEYS = new Set([38]); // Up Arrow (Slows down)
const PAGE_KEYS = new Set([33, 34]); // Page Up, Page Down (Pass-through)

let isScrolling = false, isPaused = false, scrollSpeed = 0, lastUpdateTime, realY;
const scrollElement = document.scrollingElement || document.documentElement;
let hudTimeout;

// === HUD DISPLAY ===
const hud = Object.assign(document.createElement("div"), {
    style: `position:fixed; bottom:10px; right:10px; padding:5px 10px; background:rgba(0,0,0,0.7);
            color:#fff; font-size:14px; font-family:Arial; border-radius:5px; z-index:9999; display:none`
});
document.body.appendChild(hud);

// === EVENT LISTENERS ===
window.addEventListener('keydown', (e) => {
    if (e.ctrlKey || e.shiftKey) return; // Prevent conflicts with other shortcuts

    if (e.keyCode === START_KEY && e.altKey) {
        e.preventDefault();
        isScrolling ? changeSpeed(SPEED_MULTIPLIER) : startScrolling();
    } else if (isScrolling) {
        if (e.keyCode === PAUSE_KEY) {
            e.preventDefault();
            togglePause();
        } else if (STOP_KEYS.has(e.keyCode)) {
            e.preventDefault();
            stopScrolling();
        } else if (SCROLL_DOWN_KEYS.has(e.keyCode)) {
            e.preventDefault();
            changeSpeed(SPEED_MULTIPLIER);
        } else if (SCROLL_UP_KEYS.has(e.keyCode)) {
            e.preventDefault();
            changeSpeed(SLOWDOWN_MULTIPLIER);
        } else if (PAGE_KEYS.has(e.keyCode)) {
            realY = scrollElement.scrollTop; // ✅ Updates memory only (no pausing)
        }
    } else if (isPaused) {
        if (PAGE_KEYS.has(e.keyCode) || SCROLL_DOWN_KEYS.has(e.keyCode) || SCROLL_UP_KEYS.has(e.keyCode)) {
            realY = scrollElement.scrollTop; // ✅ Updates memory but does not resume
        } else if (e.keyCode === PAUSE_KEY) {
            togglePause();
        } else if (STOP_KEYS.has(e.keyCode)) {
            e.preventDefault();
            stopScrolling();
        }
    }
}, { capture: true });

// === DETECT MOUSE WHEEL SCROLLING ===
window.addEventListener('wheel', (e) => {
    if (isScrolling) {
        e.preventDefault();
        isPaused = true;
        updateHUD();
    } else {
        realY = scrollElement.scrollTop; // ✅ Always update memory during pause
    }
}, { passive: true });

// === DETECT MANUAL PAGE UP / PAGE DOWN SCROLLING ===
window.addEventListener('scroll', () => {
    if (isPaused || isScrolling) {
        realY = scrollElement.scrollTop; // ✅ Always update memory on manual scroll
    }
}, { passive: true });

// === TOGGLE PAUSE/RESUME ===
const togglePause = () => {
    isPaused = !isPaused;
    updateHUD();
    if (!isPaused) {
        lastUpdateTime = performance.now(); // Prevents jumps after resuming
        requestAnimationFrame(scrollLoop);
    }
};

// === CHANGE SCROLL SPEED ===
const changeSpeed = (multiplier) => {
    scrollSpeed = Math.max(5, scrollSpeed * multiplier);
    updateHUD();
};

// === START AUTO-SCROLLING ===
const startScrolling = () => {
    isScrolling = true;
    isPaused = false;
    scrollSpeed = INITIAL_SPEED;
    realY = scrollElement.scrollTop;
    lastUpdateTime = performance.now();
    updateHUD();
    requestAnimationFrame(scrollLoop);
};

// === STOP AUTO-SCROLLING ===
const stopScrolling = () => {
    isScrolling = isPaused = false;
    scrollSpeed = 0;
    hud.style.display = "none";
};

// === MAIN SCROLL LOOP ===
const scrollLoop = (timestamp) => {
    if (!isScrolling || isPaused) return;
    realY += (scrollSpeed * (timestamp - lastUpdateTime)) / 1000;
    scrollElement.scrollTop = Math.floor(realY);
    lastUpdateTime = timestamp;
    requestAnimationFrame(scrollLoop);
};

// === HUD DISPLAY FUNCTION ===
const updateHUD = () => {
    hud.textContent = isPaused
        ? `PAUSED (Speed: ${scrollSpeed.toFixed(1)} px/s)`
        : `Speed: ${scrollSpeed.toFixed(1)} px/s`;
    hud.style.display = "block";
    clearTimeout(hudTimeout);
    if (!isPaused) hudTimeout = setTimeout(() => (hud.style.display = "none"), 2000);
};