// ==UserScript== // @name 自动滚动 // @namespace http://tampermonkey.net/ // @version 6.3 // @description 0.1~10滚动;鼠标移出面板透明,先主页后内嵌容器全域回顶循环 // @author You // @match *://*/* // ==/UserScript== (function() { 'use strict'; // 全局配置 const cfg = { scrollStep: 0.8, minSpeed: 0.1, maxSpeed: 4, stepGap: 0.05, bottomDelay: 600, panelOpacityNormal: 1, panelOpacityHide: 0.25 }; let isRunning = false; let rafId = null; let mainPageDone = false; let innerContainersDone = false; let scrollContainerList = []; let pixelBuffer = 0; // 像素累加缓冲区,解决小于1px不动 // 获取所有内嵌滚动容器 function getInnerScrollContainers() { const containers = []; const allNodes = document.querySelectorAll('*'); allNodes.forEach(el => { const style = getComputedStyle(el); const overflowY = style.overflowY; if ( el !== document.documentElement && el !== document.body && (overflowY === 'auto' || overflowY === 'scroll') && el.scrollHeight > el.clientHeight + 8 ) { containers.push(el); } }); return [...new Set(containers)]; } // 主页面触底判断 function checkMainPageBottom() { const scrollTop = window.scrollY; const totalHeight = document.documentElement.scrollHeight; const viewHeight = window.innerHeight; return scrollTop + viewHeight >= totalHeight - 12; } // 子容器触底判断 function isContainerBottom(el) { return el.scrollTop + el.clientHeight >= el.scrollHeight - 6; } // 全部子容器滚动完成 function allInnerDone() { return scrollContainerList.every(item => isContainerBottom(item)); } // 所有滚动区域复位回顶部 function resetAllScroll() { window.scrollTo({ top: 0, behavior: 'instant' }); scrollContainerList.forEach(el => el.scrollTop = 0); mainPageDone = false; innerContainersDone = false; pixelBuffer = 0; // 重置缓冲数值 } // 核心滚动循环:像素累加缓冲渲染 function scrollLoop() { if (!isRunning) return; // 累加微小速度值到缓冲区 pixelBuffer += cfg.scrollStep; // 攒够≥1像素,才执行滚动动作 if (pixelBuffer >= 1) { const movePx = Math.floor(pixelBuffer); pixelBuffer = pixelBuffer - movePx; // 剩余小数继续留存累加 if (!mainPageDone) { // 滚动主页面 window.scrollBy(0, movePx); if (checkMainPageBottom()) { mainPageDone = true; scrollContainerList = getInnerScrollContainers(); } } else if (!innerContainersDone) { // 滚动内嵌子容器 scrollContainerList.forEach(el => { if (!isContainerBottom(el)) { el.scrollTop += movePx; } }); if (allInnerDone()) { innerContainersDone = true; } } else { // 全部到底,延时回顶重启循环 setTimeout(() => { resetAllScroll(); if (isRunning) scrollLoop(); }, cfg.bottomDelay); return; } } rafId = requestAnimationFrame(scrollLoop); } // 启停函数 function startScroll() { cancelAnimationFrame(rafId); pixelBuffer = 0; isRunning = true; mainPageDone = false; innerContainersDone = false; startBtn.textContent = '暂停滚动'; scrollLoop(); } function stopScroll() { cancelAnimationFrame(rafId); isRunning = false; startBtn.textContent = '开始滚动'; } // 悬浮控制面板 + 移入移出透明过渡 const wrapPanel = document.createElement('div'); wrapPanel.style = ` position: fixed; right: 20px; bottom: 24px; z-index: 9999999; background: #ffffff; padding: 14px 16px; border-radius: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.15); display: flex; flex-direction: column; gap: 12px; min-width: 220px; opacity: ${cfg.panelOpacityNormal}; transition: opacity 0.35s ease; `; wrapPanel.addEventListener('mouseleave', () => { wrapPanel.style.opacity = cfg.panelOpacityHide; }); wrapPanel.addEventListener('mouseenter', () => { wrapPanel.style.opacity = cfg.panelOpacityNormal; }); // 启停按钮 const startBtn = document.createElement('button'); startBtn.style = ` width: 100%; background: #2563eb; color: #fff; border: none; padding: 9px 0; border-radius: 8px; font-size: 14px; cursor: pointer; transition: background 0.2s; `; startBtn.textContent = '开始滚动'; startBtn.onmouseover = () => startBtn.style.background = '#1d4ed8'; startBtn.onmouseout = () => startBtn.style.background = '#2563eb'; // 速度显示+滑块区域 const speedBox = document.createElement('div'); speedBox.style = `display:flex;flex-direction:column;gap:6px;`; const speedText = document.createElement('div'); speedText.style = `display:flex;justify-content:space-between;font-size:13px;color:#333;`; speedText.innerHTML = `滚动速度${cfg.scrollStep.toFixed(2)}`; const speedSlider = document.createElement('input'); speedSlider.type = 'range'; speedSlider.min = cfg.minSpeed; speedSlider.max = cfg.maxSpeed; speedSlider.step = cfg.stepGap; speedSlider.value = cfg.scrollStep; speedSlider.style = ` width: 100%; height: 6px; cursor: pointer; accent-color: #2563eb; `; speedSlider.addEventListener('input', () => { cfg.scrollStep = parseFloat(speedSlider.value); document.getElementById('speedVal').textContent = cfg.scrollStep.toFixed(2); }); speedBox.append(speedText, speedSlider); wrapPanel.append(startBtn, speedBox); document.body.appendChild(wrapPanel); startBtn.onclick = () => isRunning ? stopScroll() : startScroll(); // Alt+S 快捷键启停 document.addEventListener('keydown', e => { if (e.altKey && e.key.toLowerCase() === 's') { e.preventDefault(); isRunning ? stopScroll() : startScroll(); } }); })();