// ==UserScript==
// @name 网页滚滚滚
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 滚动到顶/底、滚动条-新、记录页面滚动、屏幕常亮和自动滚动
// @author ^o^
// @match *://*/*
// @grant GM_registerMenuCommand
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
let w = window, d = document;
let eScrollBtn = true, eScrollBar = true, eWakeLock = true;
GM_registerMenuCommand('滚动到顶/底按钮:开/关', toggleScrollBtn);
GM_registerMenuCommand('透明滚动条:开/关', toggleScrollBar);
GM_registerMenuCommand('保持屏幕常亮:开/关', toggleWakeLock);
function toggleScrollBtn() {
eScrollBtn = !eScrollBtn;
let b = d.getElementById('scroll-top-btn');
b && b.remove();
eScrollBtn && initScrollBtn();
}
function toggleScrollBar() {
eScrollBar = !eScrollBar;
let s = d.getElementById('theScrollBar');
s && s.remove();
eScrollBar && initScrollBar();
}
function toggleWakeLock() {
eWakeLock = !eWakeLock;
eWakeLock ? requestWL() : (wakeLock && wakeLock.release());
}
function initScrollBtn() {
let b = d.createElement('button');
b.textContent = '▲';
b.id = 'scroll-top-btn';
Object.assign(b.style, {
position: 'fixed', bottom: '15%', right: '15px', zIndex: 999999,
width: '35px', height: '35px', borderRadius: '50%', padding: 0,
background: 'rgba(255,255,255,0.3)', backdropFilter: 'blur(5px)',
boxShadow: '0 2px 6px rgba(0,0,0,0.2)', display: 'none',
fontSize: '16px', textAlign: 'center', lineHeight: '35px',
fontWeight: 'bold', cursor: 'pointer', transition: 'all 0.3s ease'
});
d.body.appendChild(b);
let lastY = w.pageYOffset, t;
w.addEventListener('scroll', () => {
b.textContent = w.scrollY > lastY ? '▼' : '▲';
lastY = w.scrollY;
b.style.display = w.pageYOffset > 100 ? 'block' : 'none';
clearTimeout(t);
t = setTimeout(() => b.style.display = 'none', 2000);
});
b.addEventListener('mouseenter', () => {
b.style.background = 'rgba(255,255,255,0.6)';
b.style.transform = 'scale(1.1)';
b.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)';
});
b.addEventListener('mouseleave', () => {
b.style.background = 'rgba(255,255,255,0.3)';
b.style.transform = 'scale(1)';
b.style.boxShadow = '0 2px 6px rgba(0,0,0,0.2)';
});
b.addEventListener('click', () => w.scrollTo({
top: b.textContent === '▲' ? 0 : d.documentElement.scrollHeight,
behavior: 'smooth'
}));
}
function initScrollBar() {
let s = d.createElement("div");
s.id = "theScrollBar";
s.innerHTML = "▲<br>▼";
Object.assign(s.style, {
position: 'fixed', top: '-14vw', right: '-10vw', zIndex: 9999999,
width: '6vw', fontSize: '1.5vw', lineHeight: '5vw', opacity: 0,
background: 'rgba(255,255,255,0.3)', backdropFilter: 'blur(2px)',
boxShadow: '0 1px 5px rgba(0,0,0,0.2)', borderRadius: '1vw',
textAlign: 'center', transition: 'opacity 0.4s, right 0.4s'
});
d.body.appendChild(s);
let h = null, lastTop = null, anim = false;
function updateBar() {
let st = w.scrollY;
if (st !== lastTop) {
let barTop = (st / h) * (w.innerHeight - s.clientHeight);
s.style.top = barTop < 0 ? 0 :
barTop + s.clientHeight > w.innerHeight ?
w.innerHeight - s.clientHeight + 'px' : barTop + 'px';
lastTop = st;
}
w.requestAnimationFrame(updateBar);
}
w.addEventListener("touchstart", () => {
if (d.documentElement.scrollHeight <= w.innerHeight * 2) return;
h = d.documentElement.scrollHeight - w.innerHeight;
updateBar();
});
let startY = null;
function startScroll(e) {
e.preventDefault();
startY = e.changedTouches[0].clientY - parseInt(s.style.top);
}
function scrolling(e) {
e.preventDefault();
let curY = e.changedTouches[0].clientY;
let barTop = curY - startY;
s.style.top = barTop < 0 ? 0 :
barTop > w.innerHeight - s.clientHeight ?
w.innerHeight - s.clientHeight + 'px' : barTop + 'px';
w.scrollTo(w.scrollX, (barTop / (w.innerHeight - s.clientHeight)) * h);
}
s.addEventListener("touchstart", startScroll, { passive: false });
s.addEventListener("touchmove", scrolling, { passive: false });
let t;
w.addEventListener("scroll", () => {
clearTimeout(t);
s.style.right = '2vw';
s.style.opacity = '0.8';
t = setTimeout(() => {
s.style.right = '-10vw';
s.style.opacity = '0';
}, 1000);
if (anim) return;
let st = w.scrollY, spd = Math.abs(st - lastTop);
if ((st === 0 || st + w.innerHeight >= d.documentElement.scrollHeight) && spd > 7) {
anim = true;
s.style.animation = `bounce-${st === 0 ? 'down' : 'up'} 0.4s`;
setTimeout(() => {
s.style.animation = '';
anim = false;
}, 500);
}
});
let style = d.createElement("style");
style.textContent = `@keyframes bounce-down{0%{transform:translateY(0)}30%{transform:translateY(10px)}100%{transform:translateY(0)}}@keyframes bounce-up{0%{transform:translateY(0)}30%{transform:translateY(-10px)}100%{transform:translateY(0)}}`;
d.head.appendChild(style);
}
let wakeLock = null;
async function requestWL() {
if (!('wakeLock' in navigator)) return;
try {
wakeLock = await navigator.wakeLock.request("screen");
} catch (e) {}
}
eWakeLock && requestWL();
let scrolling = false, interval, speed = 25, pixels = 1;
function toggleAutoScroll() {
if (scrolling) {
scrolling = false;
clearInterval(interval);
} else {
scrolling = true;
interval = setInterval(() => {
w.scrollBy(0, pixels);
if (w.innerHeight + w.scrollY >= d.body.scrollHeight) {
w.scrollBy(0, 1);
}
}, speed);
}
}
GM_registerMenuCommand('自动滚动:开始/停止', toggleAutoScroll);
GM_registerMenuCommand('自动滚动:配置参数', () => {
let s = prompt('滚动间隔(ms):', speed);
if (s !== null) speed = parseInt(s) || speed;
let p = prompt('每次滚动像素(px):', pixels);
if (p !== null) pixels = parseInt(p) || pixels;
});
eScrollBtn && initScrollBtn();
eScrollBar && initScrollBar();
})();