Greasy Fork

Greasy Fork is available in English.

自动滚屏助手

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name                      自动滚屏助手
// @namespace                 http://tampermonkey.net/
// @version                   1.2
// @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控制暂停/继续  按INS启用/禁用\n方向键跳跃\n<键/>键调速",
            title: "自动滚屏助手",
            timeout: 2000
        });
    }

    // 禁用自动滚屏
    function disableAutoScroll() {
        if (!isEnabled) return;
        isEnabled = false;
        turnOff();
        removeEventListeners();
        // 更新菜单状态
        GM_unregisterMenuCommand(disableCommandId);
        enableCommandId = GM_registerMenuCommand("▶ 启用自动滚屏", function() {
            enableAutoScroll();
        });
        GM_notification({
            text: "自动滚屏已禁用",
            title: "自动滚屏助手",
            timeout: 1000
        });
    }

    // 滚动逻辑
    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 getIsScrollingState() {
        return isScrolling;
    }

    // 快捷键逻辑
    function handleKeyDown(event) {
        const targetElement = event.target;
        const isInInputElement =
              targetElement.tagName === 'INPUT' ||
              targetElement.tagName === 'TEXTAREA' ||
              targetElement.tagName === 'SELECT' ||
              targetElement.isContentEditable;

        // 在输入元素中,只允许ESC和Insert键
        if (isInInputElement && event.key !== 'Escape' && event.key !== 'Insert') {
            return;
        }

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

        // 以下功能仅在启用状态下生效
        if (isEnabled) {
            // ESC键:控制暂停/继续
            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);
    }
    function removeEventListeners() {
        window.removeEventListener('keydown', handleKeyDown);
        window.removeEventListener('keyup', handleKeyUp);
    }

    // 暴露对外接口
    if (typeof window !== 'undefined') {
        unsafeWindow.turnOffAutoScroll = turnOff; // 停止滚动
        unsafeWindow.startAutoScroll = startRAFScroll; // 开始滚动
        unsafeWindow.getAutoScrollState = getIsScrollingState; // 获取当前滚动状态
    }

    //INS键控制启用/禁用
    window.addEventListener('keydown', function(event) {
        if (event.key === 'Insert') {
            if (isEnabled) {
                disableAutoScroll();
            } else {
                enableAutoScroll();
            }
            event.preventDefault();
        }
    });
    initMenu();
})();