您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Press 's' to toggle smooth auto-scroll. '[', ']' adjust speed (negative = scroll up). 'h' hides/shows HUD. Enhanced with error handling, accessibility, and configuration options.
当前为
// ==UserScript== // @name Enhanced Smooth AutoScroll // @version 2.0 // @description Press 's' to toggle smooth auto-scroll. '[', ']' adjust speed (negative = scroll up). 'h' hides/shows HUD. Enhanced with error handling, accessibility, and configuration options. // @author NAABO // @match *://*/* // @grant none // @run-at document-idle // @namespace https://gf.qytechs.cn/users/1513610 // ==/UserScript== /** * Enhanced Auto-Scroll UserScript * Hotkeys: * S → toggle auto-scroll * [/] → adjust speed (unlimited, can be negative) * H → hide/show HUD * R → reset to default speed * +/- → adjust speed step */ (function () { 'use strict'; /************* Configuration & Constants *************/ const CONFIG = { STORAGE_KEY: 'enhanced_autoscroll_config', DEFAULT_SPEED: 100, // px per second (downward) 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, // ms FLASH_DURATION: 1500, // ms 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, }; /************* Configuration Management *************/ /** * Load configuration from localStorage with error handling */ 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, using defaults', error); } } /** * Save configuration to localStorage with error handling */ 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); } } /************* Utility Functions *************/ /** * Check if user is currently typing in an input field */ 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 (error) { console.warn('Enhanced AutoScroll: Error checking typing state', error); return false; } } /** * Check if user prefers reduced motion */ function prefersReducedMotion() { try { return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; } catch (error) { return false; } } /** * Debounced function execution */ function debounce(func, delay) { return function (...args) { if (state.keyDebounceTimeout) { clearTimeout(state.keyDebounceTimeout); } state.keyDebounceTimeout = setTimeout(() => func.apply(this, args), delay); }; } /** * Safe requestAnimationFrame with fallback */ function safeRequestAnimationFrame(callback) { try { return requestAnimationFrame(callback); } catch (error) { console.warn('Enhanced AutoScroll: requestAnimationFrame not available, using setTimeout'); return setTimeout(callback, 16); // ~60fps fallback } } /** * Safe cancelAnimationFrame with fallback */ function safeCancelAnimationFrame(id) { try { cancelAnimationFrame(id); } catch (error) { clearTimeout(id); } } /************* Scrolling Engine *************/ /** * Main animation loop with error handling and accessibility checks */ function step(now) { try { if (state.terminated) return; // Check for reduced motion preference 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); // Cap delta time 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; // Check boundaries 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 { // Smooth scrolling with bounds checking 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(); } } /** * Start the animation loop with error handling */ function startLoop() { try { if (!state.rafId) { state.lastFrameTime = null; state.rafId = safeRequestAnimationFrame(step); } } catch (error) { console.error('Enhanced AutoScroll: Failed to start animation loop', error); } } /** * Stop the animation loop and clean up */ function stopLoop() { try { if (state.rafId) { safeCancelAnimationFrame(state.rafId); state.rafId = null; } state.lastFrameTime = null; } catch (error) { console.error('Enhanced AutoScroll: Failed to stop animation loop', error); } } /************* Controls *************/ /** * Toggle scroll state with optional force parameter */ function toggleEnabled(forceState) { try { 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(); } catch (error) { console.error('Enhanced AutoScroll: Failed to toggle enabled state', error); } } /** * Adjust scroll speed with bounds checking */ function adjustSpeed(delta) { try { const oldSpeed = state.speed; state.speed += delta; // Visual feedback with speed change const direction = state.speed >= 0 ? '↓' : '↑'; const speedText = `Speed: ${Math.abs(state.speed)} px/s ${direction}`; updateHUD(); flashHUD(speedText); saveConfig(); // If we were scrolling, show immediate feedback if (state.enabled && Math.sign(oldSpeed) !== Math.sign(state.speed)) { flashHUD(`Direction changed! ${speedText}`); } } catch (error) { console.error('Enhanced AutoScroll: Failed to adjust speed', error); } } /** * Reset speed to default */ function resetSpeed() { try { state.speed = CONFIG.DEFAULT_SPEED; updateHUD(); flashHUD(`Speed reset to ${CONFIG.DEFAULT_SPEED} px/s`); saveConfig(); } catch (error) { console.error('Enhanced AutoScroll: Failed to reset speed', error); } } /************* HUD Management *************/ /** * Get HUD position styles based on current position setting */ 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']; } /** * Create the HUD with enhanced styling and error handling */ function createHUD() { try { 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" style=" background:rgba(255,255,255,0.1); border:1px solid rgba(255,255,255,0.2); color:#fff; padding:4px 8px; border-radius:4px; font-size:11px; cursor:pointer; transition:all 0.2s ease; " title="Toggle scrolling">S</button> <button class="hud-btn" data-action="speed-down" style=" background:rgba(255,255,255,0.1); border:1px solid rgba(255,255,255,0.2); color:#fff; padding:4px 8px; border-radius:4px; font-size:11px; cursor:pointer; transition:all 0.2s ease; " title="Decrease speed">[</button> <button class="hud-btn" data-action="speed-up" style=" background:rgba(255,255,255,0.1); border:1px solid rgba(255,255,255,0.2); color:#fff; padding:4px 8px; border-radius:4px; font-size:11px; cursor:pointer; transition:all 0.2s ease; " title="Increase speed">]</button> <button class="hud-btn" data-action="reset" style=" background:rgba(255,255,255,0.1); border:1px solid rgba(255,255,255,0.2); color:#fff; padding:4px 8px; border-radius:4px; font-size:11px; cursor:pointer; transition:all 0.2s ease; " title="Reset speed">R</button> <button class="hud-btn" data-action="hide-hud" style=" background:rgba(255,255,255,0.1); border:1px solid rgba(255,255,255,0.2); color:#fff; padding:4px 8px; border-radius:4px; font-size:11px; cursor:pointer; transition:all 0.2s ease; " title="Hide HUD">H</button> <button class="hud-btn" data-action="step-up" style=" background:rgba(255,255,255,0.1); border:1px solid rgba(255,255,255,0.2); color:#fff; padding:4px 8px; border-radius:4px; font-size:11px; cursor:pointer; transition:all 0.2s ease; " title="Increase speed step">+</button> <button class="hud-btn" data-action="step-down" style=" background:rgba(255,255,255,0.1); border:1px solid rgba(255,255,255,0.2); color:#fff; padding:4px 8px; border-radius:4px; font-size:11px; cursor:pointer; transition:all 0.2s ease; " title="Decrease speed step">-</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); // Enhanced close button interaction const closeBtn = state.hud.querySelector('#hud-close'); closeBtn.addEventListener('click', shutdownScript); closeBtn.addEventListener('mouseenter', () => closeBtn.style.opacity = '1'); closeBtn.addEventListener('mouseleave', () => closeBtn.style.opacity = '0.7'); // Set up HUD button interactions setupHUDButtons(); } catch (error) { console.error('Enhanced AutoScroll: Failed to create HUD', error); } } /** * Set up HUD button event listeners with direct function calls */ function setupHUDButtons() { try { const buttons = state.hud.querySelectorAll('.hud-btn'); buttons.forEach(button => { const action = button.getAttribute('data-action'); // Add hover effects button.addEventListener('mouseenter', () => { button.style.background = 'rgba(255,255,255,0.2)'; button.style.borderColor = 'rgba(255,255,255,0.4)'; }); button.addEventListener('mouseleave', () => { button.style.background = 'rgba(255,255,255,0.1)'; button.style.borderColor = 'rgba(255,255,255,0.2)'; }); // Add click handlers - direct function calls, no key simulation 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 += 1; saveConfig(); updateHUD(); flashHUD(`Speed step: ${state.speedStep}`); } break; case 'step-down': if (state.speedStep > CONFIG.MIN_SPEED_STEP) { state.speedStep -= 1; saveConfig(); updateHUD(); flashHUD(`Speed step: ${state.speedStep}`); } break; } }); }); } catch (error) { console.error('Enhanced AutoScroll: Failed to setup HUD buttons', error); } } /** * Update HUD content with current state */ function updateHUD() { try { 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'); // Update status if (state.enabled) { const direction = state.speed >= 0 ? '↓' : '↑'; statusEl.textContent = `SCROLLING ${direction}`; statusEl.style.color = '#4ade80'; } else { statusEl.textContent = 'PAUSED'; statusEl.style.color = '#ef4444'; } // Update speed info speedEl.textContent = `Speed: ${Math.abs(state.speed)} px/s (Step: ${state.speedStep})`; // Update config info const configParts = []; if (state.respectsReducedMotion && prefersReducedMotion()) configParts.push('Reduced motion'); configEl.textContent = configParts.join(' • '); // Update button states updateButtonStates(); } catch (error) { console.error('Enhanced AutoScroll: Failed to update HUD', error); } } /** * Update button visual states based on current settings */ function updateButtonStates() { try { if (!state.hud) return; const buttons = state.hud.querySelectorAll('.hud-btn'); buttons.forEach(button => { const action = button.getAttribute('data-action'); // Highlight active states if (action === 'toggle' && state.enabled) { button.style.background = 'rgba(76, 175, 80, 0.3)'; button.style.borderColor = 'rgba(76, 175, 80, 0.6)'; } else if (action === 'toggle' && !state.enabled) { button.style.background = 'rgba(244, 67, 54, 0.3)'; button.style.borderColor = 'rgba(244, 67, 54, 0.6)'; } }); } catch (error) { console.error('Enhanced AutoScroll: Failed to update button states', error); } } /** * Flash HUD with temporary message */ function flashHUD(text, duration = CONFIG.FLASH_DURATION) { try { if (!state.hud) createHUD(); if (!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(() => { try { updateHUD(); state.flashTimeout = null; } catch (error) { console.error('Enhanced AutoScroll: Error in flash timeout', error); } }, duration); } catch (error) { console.error('Enhanced AutoScroll: Failed to flash HUD', error); } } /************* Enhanced Shutdown *************/ /** * Comprehensive script shutdown with cleanup */ function shutdownScript() { try { if (state.terminated) return; state.terminated = true; // Stop all animations and timers stopLoop(); state.enabled = false; // Clear all timeouts if (state.flashTimeout) { clearTimeout(state.flashTimeout); state.flashTimeout = null; } if (state.keyDebounceTimeout) { clearTimeout(state.keyDebounceTimeout); state.keyDebounceTimeout = null; } // Remove all event listeners document.removeEventListener('keydown', onKeyDown); window.removeEventListener('beforeunload', shutdownScript); // Remove HUD with fade effect if (state.hud) { state.hud.style.transition = 'opacity 0.3s ease'; state.hud.style.opacity = '0'; setTimeout(() => { if (state.hud && state.hud.parentNode) { state.hud.remove(); } state.hud = null; }, 300); } console.log('Enhanced AutoScroll: Script terminated successfully'); } catch (error) { console.error('Enhanced AutoScroll: Error during shutdown', error); } } /************* Enhanced Key Handling *************/ /** * Debounced key handler with comprehensive hotkey support */ const onKeyDown = debounce(function (e) { try { if (isTyping(e) || state.terminated) return; const key = e.key.toLowerCase(); let handled = false; switch (key) { case 's': e.preventDefault(); toggleEnabled(); handled = true; break; case '[': e.preventDefault(); adjustSpeed(-state.speedStep); handled = true; break; case ']': e.preventDefault(); adjustSpeed(state.speedStep); handled = true; break; case 'h': e.preventDefault(); state.hudVisible = !state.hudVisible; updateHUD(); flashHUD(`HUD ${state.hudVisible ? 'shown' : 'hidden'}`); handled = true; break; case 'r': e.preventDefault(); resetSpeed(); handled = true; break; case '+': case '=': e.preventDefault(); if (state.speedStep < CONFIG.MAX_SPEED_STEP) { state.speedStep += 1; saveConfig(); updateHUD(); flashHUD(`Speed step: ${state.speedStep}`); } handled = true; break; case '-': case '_': e.preventDefault(); if (state.speedStep > CONFIG.MIN_SPEED_STEP) { state.speedStep -= 1; saveConfig(); updateHUD(); flashHUD(`Speed step: ${state.speedStep}`); } handled = true; break; } if (handled) { e.stopPropagation(); } } catch (error) { console.error('Enhanced AutoScroll: Error in key handler', error); } }, CONFIG.DEBOUNCE_DELAY); /************* Enhanced Initialization *************/ /** * Initialize the enhanced autoscroll with comprehensive setup */ function init() { try { console.log('Enhanced AutoScroll: Initializing v2.0'); // Load saved configuration loadConfig(); // Create and update HUD createHUD(); updateHUD(); // Set up event listeners with error handling document.addEventListener('keydown', onKeyDown, { passive: false }); window.addEventListener('beforeunload', shutdownScript, { passive: true }); // Show initialization message flashHUD('Enhanced AutoScroll ready! Press S to start', 2000); console.log('Enhanced AutoScroll: Initialization complete'); } catch (error) { console.error('Enhanced AutoScroll: Failed to initialize', error); // Try to show error in a basic way try { alert('Enhanced AutoScroll failed to initialize. Check console for details.'); } catch (alertError) { // If even alert fails, just log console.error('Enhanced AutoScroll: Critical initialization failure'); } } } // Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址