Warning: fopen(/www/sites/update.greasyfork.icu/index/store/temp/2ae31693b26763ff44eb774fe2604622.js): failed to open stream: No space left on device in /www/sites/update.greasyfork.icu/index/scriptControl.php on line 65
// ==UserScript==
// @name 滚动条及顶部底部按钮
// @namespace https://greasyfork.org/
// @version 1.2
// @description 在任何网页顶部显示滚动进度条,并提供滚动到顶部和底部的按钮,优化性能,减少卡顿现象。
// @author Leo
// @match *://*/*
// @grant none
// @license MIT
// @downloadURL https://update.greasyfork.icu/scripts/511644/%E6%BB%9A%E5%8A%A8%E6%9D%A1%E5%8F%8A%E9%A1%B6%E9%83%A8%E5%BA%95%E9%83%A8%E6%8C%89%E9%92%AE.user.js
// @updateURL https://update.greasyfork.icu/scripts/511644/%E6%BB%9A%E5%8A%A8%E6%9D%A1%E5%8F%8A%E9%A1%B6%E9%83%A8%E5%BA%95%E9%83%A8%E6%8C%89%E9%92%AE.meta.js
// ==/UserScript==
(function () {
'use strict';
// 缓存 DOM 元素和变量
const { documentElement: docEl, body } = document;
let pageHeight = 0, clientHeight = 0, maxScroll = 0;
let lastScrollTop = -1, lastMaxScroll = -1;
// 进度条类
class ProgressBar {
constructor() {
this._progress = -1;
this.bar = this._createBar();
}
_createBar() {
const progressBar = document.createElement('div');
progressBar.id = 'progress-bar';
progressBar.style.cssText = `
position: fixed; top: 0; left: 0; width: 100%; height: 2px; transform: scaleX(0);
background: linear-gradient(90deg, #03a9f4, #f441a5); z-index: 999999999;
pointer-events: none; transform-origin: left;
box-shadow: 0 0 40px #03a9f4, 0 0 50px #f441a5, 0 0 60px #03a9f4;
border-radius: 0.5em; transition: transform 0.2s ease;
`;
document.body.insertBefore(progressBar, document.body.firstChild);
return progressBar;
}
update(progress) {
if (this._progress !== progress) {
this.bar.style.transform = `scaleX(${progress / 100})`;
this._progress = progress;
}
}
}
// 滚动按钮类
class ScrollButtons {
constructor() {
this._visible = false;
this.buttons = this._createButtons();
}
_createButtons() {
const scrollBtns = document.createElement('div');
scrollBtns.className = 'scroll-buttons';
scrollBtns.style.cssText = `
position: fixed; bottom: 10px; right: 5rem; z-index: 1000000000; display: none;
`;
['⬆️顶部', '⬇️最新'].forEach((text, i) => {
const button = document.createElement('button');
button.innerText = text;
button.onclick = () => smoothScroll(i ? maxScroll : 0);
button.style.cssText = `
background-color: #0005; color: #eee; border: none; font-size: 16px;
padding: 10px; margin: 0 5px; border-radius: 5px; cursor: pointer;
`;
scrollBtns.appendChild(button);
});
document.body.appendChild(scrollBtns);
return scrollBtns;
}
update(scrollTop) {
const showScrollButtons = maxScroll > 0;
if (this._visible !== showScrollButtons) {
this.buttons.style.display = showScrollButtons ? 'flex' : 'none';
this._visible = showScrollButtons;
}
// 当在顶部时隐藏顶部按钮,当在底部时隐藏底部按钮
this.buttons.children[0].style.display = scrollTop > 100 ? 'inline-block' : 'none';
this.buttons.children[1].style.display = (maxScroll - scrollTop > 100) ? 'inline-block' : 'none';
}
}
// 初始化进度条和滚动按钮
const progressBar = new ProgressBar();
const scrollButtons = new ScrollButtons();
// 更新页面尺寸
const updatePageDimensions = () => {
const newClientHeight = docEl.clientHeight;
const newPageHeight = Math.max(
body.scrollHeight, docEl.scrollHeight,
body.offsetHeight, docEl.offsetHeight,
body.clientHeight, docEl.clientHeight
);
if (newPageHeight !== pageHeight || newClientHeight !== clientHeight) {
clientHeight = newClientHeight;
pageHeight = newPageHeight;
maxScroll = pageHeight - clientHeight;
return true;
}
return false;
};
// 节流的滚动处理器
const scrollHandler = throttle(() => {
requestAnimationFrame(updateUI);
}, 16);
// 防抖的调整大小处理器
const resizeHandler = debounce(() => {
if (updatePageDimensions()) updateUI();
}, 100);
// 添加事件监听器
const addListeners = () => {
window.addEventListener('scroll', scrollHandler, { passive: true });
window.addEventListener('resize', resizeHandler, { passive: true });
};
// 更新 UI(进度条和滚动按钮)
function updateUI() {
const scrollTop = window.scrollY;
if (scrollTop === lastScrollTop && maxScroll === lastMaxScroll) return;
lastScrollTop = scrollTop;
lastMaxScroll = maxScroll;
const progress = maxScroll ? (scrollTop / maxScroll) * 100 : 0;
progressBar.update(progress);
scrollButtons.update(scrollTop);
}
// 平滑滚动到目标
function smoothScroll(target) {
const start = window.scrollY;
const change = target - start;
const duration = 400;
const increment = 16;
const animateScroll = (startTime) => {
const currentTime = performance.now() - startTime;
const val = easeInOutQuad(currentTime, start, change, duration);
window.scrollTo(0, val);
if (currentTime < duration) requestAnimationFrame(() => animateScroll(startTime));
};
requestAnimationFrame((startTime) => animateScroll(startTime));
}
// 缓动函数
function easeInOutQuad(t, b, c, d) {
t /= d / 2;
return t < 1 ? c / 2 * t * t + b : -c / 2 * (--t * (t - 2) - 1) + b;
}
// 节流函数
function throttle(fn, delay) {
let lastTime = 0;
return (...args) => {
const now = performance.now();
if (now - lastTime >= delay) {
lastTime = now;
fn(...args);
}
};
}
// 防抖函数
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// 初始化函数
function init() {
updatePageDimensions();
updateUI();
addListeners();
}
// 执行初始化
init();
// 清理函数
const cleanup = () => {
window.removeEventListener('scroll', scrollHandler);
window.removeEventListener('resize', resizeHandler);
};
// 添加清理监听器
window.addEventListener('beforeunload', cleanup);
// 使用 IntersectionObserver 替代部分滚动监听
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 处理元素进入视口的逻辑
}
});
});
// 缓存 DOM 元素
const progressBarElement = document.getElementById('progress-bar');
const scrollButtonsElement = document.querySelector('.scroll-buttons');
// 使用 CSS 类切换
function updateProgressBar(progress) {
if (progressBarElement) {
progressBarElement.style.transform = `scaleX(${progress / 100})`;
}
}
function updateScrollButtonsVisibility(visible) {
if (scrollButtonsElement) {
scrollButtonsElement.classList.toggle('visible', visible);
}
}
// 使用 passive 事件监听
window.addEventListener('scroll', scrollHandler, { passive: true });
})();