您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Press 's' to toggle smooth auto-scroll. '[', ']' adjust speed. 'h' hides HUD. 'r' reset speed. '+/-' adjust step. 🚫 Block sites via HUD.
当前为
// ==UserScript== // @name Enhanced Smooth AutoScroll // @version 2.1 // @description Press 's' to toggle smooth auto-scroll. '[', ']' adjust speed. 'h' hides HUD. 'r' reset speed. '+/-' adjust step. 🚫 Block sites via HUD. // @author NAABO // @match *://*/* // @grant none // @run-at document-idle // @namespace https://gf.qytechs.cn/users/1513610 // ==/UserScript== (function () { 'use strict'; /************* Configuration & Constants *************/ const CONFIG = { STORAGE_KEY: 'enhanced_autoscroll_config', BLOCKLIST_KEY: 'enhanced_autoscroll_blocklist', DEFAULT_SPEED: 100, DEFAULT_SPEED_STEP: 10, MIN_SPEED_STEP: 1, MAX_SPEED_STEP: 50, HUD_POSITIONS: ['bottom-right', 'bottom-left', 'top-right', 'top-left'], DEBOUNCE_DELAY: 100, FLASH_DURATION: 1500, EASING_FACTOR: 0.2, }; /************* State Management *************/ let state = { enabled: false, speed: CONFIG.DEFAULT_SPEED, speedStep: CONFIG.DEFAULT_SPEED_STEP, lastFrameTime: null, rafId: null, hud: null, hudVisible: true, hudPosition: 'bottom-right', terminated: false, flashTimeout: null, keyDebounceTimeout: null, respectsReducedMotion: true, }; /************* Config Management *************/ function loadConfig() { try { const saved = localStorage.getItem(CONFIG.STORAGE_KEY); if (saved) { const config = JSON.parse(saved); state.speed = Number(config.speed) || CONFIG.DEFAULT_SPEED; state.speedStep = Number(config.speedStep) || CONFIG.DEFAULT_SPEED_STEP; state.hudPosition = config.hudPosition || 'bottom-right'; state.respectsReducedMotion = Boolean(config.respectsReducedMotion); } } catch (error) { console.warn('Enhanced AutoScroll: Failed to load config', error); } } function saveConfig() { try { const config = { speed: state.speed, speedStep: state.speedStep, hudPosition: state.hudPosition, respectsReducedMotion: state.respectsReducedMotion, }; localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify(config)); } catch (error) { console.warn('Enhanced AutoScroll: Failed to save config', error); } } function loadBlocklist() { try { const saved = localStorage.getItem(CONFIG.BLOCKLIST_KEY); return saved ? JSON.parse(saved) : []; } catch (error) { console.warn('Enhanced AutoScroll: Failed to load blocklist', error); return []; } } function saveBlocklist(list) { try { localStorage.setItem(CONFIG.BLOCKLIST_KEY, JSON.stringify(list)); } catch (error) { console.warn('Enhanced AutoScroll: Failed to save blocklist', error); } } /************* Utility Functions *************/ function isTyping(event) { try { const tgt = event.target; if (!tgt) return false; const tag = (tgt.tagName || '').toLowerCase(); if (tag === 'input' || tag === 'textarea') return true; if (tgt.isContentEditable) return true; return false; } catch { return false; } } function prefersReducedMotion() { try { return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; } catch { return false; } } function debounce(func, delay) { return function (...args) { if (state.keyDebounceTimeout) clearTimeout(state.keyDebounceTimeout); state.keyDebounceTimeout = setTimeout(() => func.apply(this, args), delay); }; } function safeRequestAnimationFrame(callback) { try { return requestAnimationFrame(callback); } catch { return setTimeout(callback, 16); } } function safeCancelAnimationFrame(id) { try { cancelAnimationFrame(id); } catch { clearTimeout(id); } } /************* Scrolling Engine *************/ function step(now) { try { if (state.terminated) return; if (state.respectsReducedMotion && prefersReducedMotion()) { if (state.enabled) { toggleEnabled(false); flashHUD('Paused: Reduced motion preferred'); } return; } if (!state.lastFrameTime) state.lastFrameTime = now; const dt = Math.min((now - state.lastFrameTime) / 1000, 0.1); state.lastFrameTime = now; if (state.enabled) { const delta = state.speed * dt; const maxScroll = Math.max(0, document.documentElement.scrollHeight - window.innerHeight); const currentY = window.scrollY || window.pageYOffset || 0; if (state.speed > 0 && currentY >= Math.floor(maxScroll)) { toggleEnabled(false); flashHUD('End of page reached'); } else if (state.speed < 0 && currentY <= 0) { toggleEnabled(false); flashHUD('Top of page reached'); } else { const newY = Math.max(0, Math.min(maxScroll, currentY + delta)); window.scrollTo({ top: newY, behavior: 'instant' }); } } if (!state.terminated) state.rafId = safeRequestAnimationFrame(step); } catch (error) { console.error('Enhanced AutoScroll: Error in animation step', error); stopLoop(); } } function startLoop() { if (!state.rafId) { state.lastFrameTime = null; state.rafId = safeRequestAnimationFrame(step); } } function stopLoop() { if (state.rafId) { safeCancelAnimationFrame(state.rafId); state.rafId = null; } state.lastFrameTime = null; } /************* Controls *************/ function toggleEnabled(forceState) { if (typeof forceState === 'boolean') state.enabled = forceState; else state.enabled = !state.enabled; updateHUD(); if (state.enabled) { startLoop(); flashHUD(`Scrolling ${state.speed >= 0 ? 'down' : 'up'} at ${Math.abs(state.speed)} px/s`); } else { stopLoop(); flashHUD('Scrolling paused'); } saveConfig(); } function adjustSpeed(delta) { const oldSpeed = state.speed; state.speed += delta; const direction = state.speed >= 0 ? '↓' : '↑'; const speedText = `Speed: ${Math.abs(state.speed)} px/s ${direction}`; updateHUD(); flashHUD(speedText); saveConfig(); if (state.enabled && Math.sign(oldSpeed) !== Math.sign(state.speed)) { flashHUD(`Direction changed! ${speedText}`); } } function resetSpeed() { state.speed = CONFIG.DEFAULT_SPEED; updateHUD(); flashHUD(`Speed reset to ${CONFIG.DEFAULT_SPEED} px/s`); saveConfig(); } /************* HUD *************/ function getHUDPositionStyles() { const positions = { 'bottom-right': 'right:12px; bottom:12px;', 'bottom-left': 'left:12px; bottom:12px;', 'top-right': 'right:12px; top:12px;', 'top-left': 'left:12px; top:12px;', }; return positions[state.hudPosition] || positions['bottom-right']; } function createHUD() { if (state.hud) state.hud.remove(); state.hud = document.createElement('div'); state.hud.setAttribute('id', 'enhanced-autoscroll-hud'); const positionStyles = getHUDPositionStyles(); state.hud.style.cssText = ` position:fixed; ${positionStyles} z-index:999999; padding:10px 14px 12px 14px; background:rgba(0,0,0,0.75); color:#fff; font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial; font-size:13px; border-radius:10px; box-shadow:0 8px 24px rgba(0,0,0,0.6); backdrop-filter:blur(8px); max-width:340px; line-height:1.3; pointer-events:auto; opacity:0.95; transition:all 0.2s ease; border:1px solid rgba(255,255,255,0.1); `; state.hud.innerHTML = ` <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;"> <div id="hud-status" style="font-weight:600; font-size:14px;">PAUSED</div> <button id="hud-close" style=" background:none; border:none; color:#fff; font-size:16px; cursor:pointer; padding:2px 6px; line-height:1; opacity:0.7; border-radius:4px; transition:opacity 0.2s ease; " title="Close Enhanced AutoScroll">×</button> </div> <div id="hud-speed" style="margin-bottom:8px; font-size:12px; opacity:0.9;"></div> <div id="hud-config" style="margin-bottom:8px; font-size:11px; opacity:0.8;"></div> <div id="hud-buttons" style="margin-bottom:8px; display:flex; gap:4px; flex-wrap:wrap;"> <button class="hud-btn" data-action="toggle">S</button> <button class="hud-btn" data-action="speed-down">[</button> <button class="hud-btn" data-action="speed-up">]</button> <button class="hud-btn" data-action="reset">R</button> <button class="hud-btn" data-action="hide-hud">H</button> <button class="hud-btn" data-action="step-up">+</button> <button class="hud-btn" data-action="step-down">-</button> <button class="hud-btn" data-action="block-site" title="Block this site">🚫</button> </div> <div style="font-size:9px; opacity:0.7; line-height:1.3;"> Click buttons above or use keyboard shortcuts </div> `; document.body.appendChild(state.hud); state.hud.querySelector('#hud-close').addEventListener('click', shutdownScript); setupHUDButtons(); } function setupHUDButtons() { const buttons = state.hud.querySelectorAll('.hud-btn'); buttons.forEach(button => { const action = button.getAttribute('data-action'); button.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); switch (action) { case 'toggle': toggleEnabled(); break; case 'speed-down': adjustSpeed(-state.speedStep); break; case 'speed-up': adjustSpeed(state.speedStep); break; case 'reset': resetSpeed(); break; case 'hide-hud': state.hudVisible = false; updateHUD(); flashHUD('HUD hidden - Press H to show', 3000); break; case 'step-up': if (state.speedStep < CONFIG.MAX_SPEED_STEP) { state.speedStep++; saveConfig(); updateHUD(); flashHUD(`Speed step: ${state.speedStep}`); } break; case 'step-down': if (state.speedStep > CONFIG.MIN_SPEED_STEP) { state.speedStep--; saveConfig(); updateHUD(); flashHUD(`Speed step: ${state.speedStep}`); } break; case 'block-site': const domain = window.location.hostname; if (confirm(`Block Enhanced AutoScroll on ${domain}?`)) { const list = loadBlocklist(); if (!list.includes(domain)) { list.push(domain); saveBlocklist(list); } flashHUD(`Blocked on ${domain}`); shutdownScript(); } break; } }); }); } function updateHUD() { if (!state.hud) createHUD(); if (!state.hudVisible) { state.hud.style.display = 'none'; return; } state.hud.style.display = 'block'; const statusEl = state.hud.querySelector('#hud-status'); const speedEl = state.hud.querySelector('#hud-speed'); const configEl = state.hud.querySelector('#hud-config'); if (state.enabled) { const direction = state.speed >= 0 ? '↓' : '↑'; statusEl.textContent = `SCROLLING ${direction}`; statusEl.style.color = '#4ade80'; } else { statusEl.textContent = 'PAUSED'; statusEl.style.color = '#ef4444'; } speedEl.textContent = `Speed: ${Math.abs(state.speed)} px/s (Step: ${state.speedStep})`; configEl.textContent = (state.respectsReducedMotion && prefersReducedMotion()) ? 'Reduced motion' : ''; } function flashHUD(text, duration = CONFIG.FLASH_DURATION) { if (!state.hud || !state.hudVisible) return; const statusEl = state.hud.querySelector('#hud-status'); const originalText = statusEl.textContent; const originalColor = statusEl.style.color; statusEl.textContent = text; statusEl.style.color = '#60a5fa'; if (state.flashTimeout) clearTimeout(state.flashTimeout); state.flashTimeout = setTimeout(() => updateHUD(), duration); } /************* Shutdown *************/ function shutdownScript() { if (state.terminated) return; state.terminated = true; stopLoop(); state.enabled = false; if (state.flashTimeout) clearTimeout(state.flashTimeout); if (state.keyDebounceTimeout) clearTimeout(state.keyDebounceTimeout); document.removeEventListener('keydown', onKeyDown); window.removeEventListener('beforeunload', shutdownScript); if (state.hud) { state.hud.remove(); state.hud = null; } console.log('Enhanced AutoScroll: Script terminated'); } /************* Key Handling *************/ const onKeyDown = debounce(function (e) { if (isTyping(e) || state.terminated) return; const key = e.key.toLowerCase(); switch (key) { case 's': e.preventDefault(); toggleEnabled(); break; case '[': e.preventDefault(); adjustSpeed(-state.speedStep); break; case ']': e.preventDefault(); adjustSpeed(state.speedStep); break; case 'h': e.preventDefault(); state.hudVisible = !state.hudVisible; updateHUD(); flashHUD(`HUD ${state.hudVisible ? 'shown' : 'hidden'}`); break; case 'r': e.preventDefault(); resetSpeed(); break; case '+': case '=': e.preventDefault(); if (state.speedStep < CONFIG.MAX_SPEED_STEP) { state.speedStep++; saveConfig(); updateHUD(); flashHUD(`Speed step: ${state.speedStep}`); } break; case '-': case '_': e.preventDefault(); if (state.speedStep > CONFIG.MIN_SPEED_STEP) { state.speedStep--; saveConfig(); updateHUD(); flashHUD(`Speed step: ${state.speedStep}`); } break; } }, CONFIG.DEBOUNCE_DELAY); /************* Initialization *************/ function init() { const blocklist = loadBlocklist(); const domain = window.location.hostname; if (blocklist.includes(domain)) { console.log(`Enhanced AutoScroll: Disabled on blocked site ${domain}`); return; } loadConfig(); createHUD(); updateHUD(); document.addEventListener('keydown', onKeyDown, { passive: false }); window.addEventListener('beforeunload', shutdownScript, { passive: true }); flashHUD('Enhanced AutoScroll ready! Press S to start', 2000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址