// ==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();
}
});
})();