滚动条及顶部底部按钮

在任何网页顶部显示滚动进度条,并提供滚动到顶部和底部的按钮,优化性能,减少卡顿现象。

// ==UserScript==
// @name         滚动条及顶部底部按钮
// @namespace    https://gf.qytechs.cn/
// @version      1.2
// @description  在任何网页顶部显示滚动进度条,并提供滚动到顶部和底部的按钮,优化性能,减少卡顿现象。
// @author       Leo
// @match        *://*/*
// @grant        none
// @license      MIT
// ==/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 });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址