Greasy Fork

Greasy Fork is available in English.

自动滚屏助手

一个带有开关控制的自动滚屏助手,按 ESC 暂停/继续, 上/下方向键跳跃, <小于号键/>大于号键调速。左右方向键未设置,避免与翻页冲突

当前为 2025-04-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         自动滚屏助手
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  一个带有开关控制的自动滚屏助手,按 ESC 暂停/继续, 上/下方向键跳跃, <小于号键/>大于号键调速。左右方向键未设置,避免与翻页冲突
// @author       coccvo
// @match        *://*/*
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAeBJREFUWEftl1lKxEAURU87oB/qFhxAxQHB7TigbqN//NJlOKMLcsIBpy3oh7P2hVdShqSqkg6I0PWTpqty38nLe1U3Df54NCrG7wVGAV013oBbu5aSLAMwDJwAA5EIT8A8cJ1CkgqgwDOe4BdwBbzbfz3AOODrXQKTMYgUgGegz4RWgL2IqNbs2JoXoD+0PgZwBkwBeuKu2NNk5j8tI9Lws/drWQhgxApLN8RAi9gEriGt+7xFIeFHK7iUtBcBrALbgLSGygI4+hDkhhXieuD1BHWKxNXfrwnvPgXS1YI0Xdf88BYBTAAXQKyVUgCkIz21pPSSilBVq94PVrBlKFakp8A0MAvodwegk4H/nYFuw/+wa7YNs/NaVmsXZPd2H0B+4c7A/P2lVoD9ltFYsiByRHJBGgruDpvDlldY9F50rQDSlR9YLtj7j1q73UJmrhJAbCv2M+HiZZ/c/V9pK045jA68VBcFF0Slw0g3phw0m/aYzbqPY+k5QyJTsRsIEJpaA7aqGpI6LZm65CGPNOb1XAW3Y0qloaM4d8QAdJNvy5VSZ7mLNJ0P1HzbttwFqfJhcm5GJFg+KRlwAnqPx8BgpCBVvHPetlwbgC+kT7GxzMfpTZ7pjHVPmQzEtCrNfwMMv4whwzA6nAAAAABJRU5ErkJggg==
// @license MIT
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_notification
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // 滚动控制变量
    let isEnabled = false;
    let isScrolling = false;
    let animationFrameId;
    let lastTimestamp = 0;
    let pixelsPerSecond = 70;
    const speedAdjustAmount = 3;
    const jumpIncrement = 200;
    const maxPixelsPerSecond = 800;
    const minPixelsPerSecond = 59;
    const pressedKeys = new Set();

    // 菜单命令ID
    let enableCommandId, disableCommandId;

    // 初始化菜单
    function initMenu() {
        // 注册"启用自动滚屏"命令
        enableCommandId = GM_registerMenuCommand("▶ 启用自动滚屏", function() {
            enableAutoScroll();
        });

        // 注册"禁用自动滚屏"命令(初始时禁用)
        disableCommandId = GM_registerMenuCommand("⏸ 禁用自动滚屏", function() {
            disableAutoScroll();
        }, { autoClose: true });

        // 初始状态禁用"禁用"命令
        GM_unregisterMenuCommand(disableCommandId);
    }

    // 启用自动滚屏
    function enableAutoScroll() {
        if (isEnabled) return;

        isEnabled = true;
        initAutoScroll();
        startRAFScroll();

        // 更新菜单状态
        GM_unregisterMenuCommand(enableCommandId);
        disableCommandId = GM_registerMenuCommand("⏸ 禁用自动滚屏", function() {
            disableAutoScroll();
        }, { autoClose: true });

        GM_notification({
            text: "自动滚屏已启用\n按ESC控制暂停/继续\n方向键跳跃\n<键/>键调速",
            title: "自动滚屏助手",
            timeout: 3000
        });
    }

    // 禁用自动滚屏
    function disableAutoScroll() {
        if (!isEnabled) return;

        isEnabled = false;
        turnOff();
        removeEventListeners();

        // 更新菜单状态
        GM_unregisterMenuCommand(disableCommandId);
        enableCommandId = GM_registerMenuCommand("▶ 启用自动滚屏", function() {
            enableAutoScroll();
        });

        GM_notification({
            text: "自动滚屏已禁用",
            title: "自动滚屏助手",
            timeout: 2000
        });
    }

    // 滚动逻辑
    function scrollStepRAF(timestamp) {
        if (!isScrolling || !isEnabled) return;

        if (lastTimestamp === 0) {
            lastTimestamp = timestamp;
            animationFrameId = requestAnimationFrame(scrollStepRAF);
            return;
        }

        const deltaTime = (timestamp - lastTimestamp) / 1000;
        lastTimestamp = timestamp;
        const scrollAmount = pixelsPerSecond * deltaTime;
        window.scrollBy(0, scrollAmount);
        animationFrameId = requestAnimationFrame(scrollStepRAF);
    }

    function startRAFScroll() {
        if ((isScrolling && animationFrameId) || !isEnabled) return;
        isScrolling = true;
        lastTimestamp = 0;
        animationFrameId = requestAnimationFrame(scrollStepRAF);
    }

    function turnOff() {
        if (!isScrolling) return;
        isScrolling = false;
        if (animationFrameId) {
            cancelAnimationFrame(animationFrameId);
            animationFrameId = null;
        }
    }

    function handleKeyDown(event) {
        if (!isEnabled) return;

        const targetElement = event.target;
        const isInInputElement = targetElement.tagName === 'INPUT' ||
                               targetElement.tagName === 'TEXTAREA' ||
                               targetElement.tagName === 'SELECT' ||
                               targetElement.isContentEditable;
        if (isInInputElement && event.key !== 'Escape') {
            return;
        }

        pressedKeys.add(event.key);
        let handled = false;

        if (pressedKeys.has('Escape')) {
            if (isScrolling) {
                turnOff();
                console.log("自动滚屏: 已暂停 (ESC)");
            } else {
                startRAFScroll();
                console.log("自动滚屏: 已启动 (ESC)");
            }
            handled = true;
        }
        if (pressedKeys.has('ArrowUp')) {
            window.scrollBy(0, -jumpIncrement);
            handled = true;
        }
        if (pressedKeys.has('ArrowDown')) {
            window.scrollBy(0, jumpIncrement);
            handled = true;
        }
        if (pressedKeys.has('.')) {
            pixelsPerSecond += speedAdjustAmount;
            if (pixelsPerSecond > maxPixelsPerSecond) pixelsPerSecond = maxPixelsPerSecond;
            console.log("自动滚屏: 速度", pixelsPerSecond, "px/s");
            handled = true;
        }
        if (pressedKeys.has(',')) {
            pixelsPerSecond -= speedAdjustAmount;
            if (pixelsPerSecond < minPixelsPerSecond) pixelsPerSecond = minPixelsPerSecond;
            console.log("自动滚屏: 速度", pixelsPerSecond, "px/s");
            handled = true;
        }

        if (handled) {
            event.preventDefault();
        }
    }

    function handleKeyUp(event) {
        pressedKeys.delete(event.key);
    }

    function initAutoScroll() {
        window.addEventListener('keydown', handleKeyDown);
        window.addEventListener('keyup', handleKeyUp, false);
        window.turnOffAutoScroll_A_RAF = turnOff;
        window.startAutoScroll_A_RAF = startRAFScroll;
    }

    function removeEventListeners() {
        window.removeEventListener('keydown', handleKeyDown);
        window.removeEventListener('keyup', handleKeyUp);
    }

    // 初始化菜单
    initMenu();
})();