油猴脚本实现全网页通用的返回顶部功能

image-20260130222112113

一个油猴脚本,在任意所有网页右下角都添加一个向上返回的箭头。专业的做法,使用jQuery。一个专业的油猴脚本,实现全网页通用的返回顶部功能。

javascript

// ==UserScript==
// @name         Professional Back to Top Button
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  专业的返回顶部按钮 - 支持所有网页
// @author       Your Name
// @match        *://*/*
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNNyAxNGw1LTUgNSA1eiIvPjwvc3ZnPg==
// @grant        GM_addStyle
// @require      https://code.jquery.com/jquery-3.7.1.min.js
// @run-at       document-idle
// ==/UserScript==

(function($) {
    'use strict';

    /**
     * 返回顶部模块
     */
    var BackToTop = {
        // 配置项
        config: {
            scrollThreshold: 300,        // 滚动多少像素后显示按钮
            animationDuration: 600,      // 返回顶部动画时长
            buttonSize: 50,              // 按钮大小
            bottomOffset: 30,            // 距离底部距离
            rightOffset: 30,             // 距离右侧距离
            zIndex: 999999,              // z-index 层级
            showDelay: 100,              // 显示延迟
            hideDelay: 100               // 隐藏延迟
        },

        // 状态
        state: {
            isVisible: false,
            isAnimating: false,
            scrollTimer: null
        },

        // DOM 元素
        $elements: {},

        /**
         * 初始化
         */
        init: function() {
            this.injectStyles();
            this.createButton();
            this.cacheElements();
            this.bindEvents();
            this.checkScrollPosition();

            console.log('[BackToTop] Initialized successfully');
            return this;
        },

        /**
         * 注入 CSS 样式
         */
        injectStyles: function() {
            var css = `
                /* 返回顶部按钮容器 */
                #btt-button {
                    position: fixed;
                    bottom: ${this.config.bottomOffset}px;
                    right: ${this.config.rightOffset}px;
                    width: ${this.config.buttonSize}px;
                    height: ${this.config.buttonSize}px;
                    z-index: ${this.config.zIndex};
                    cursor: pointer;
                    opacity: 0;
                    visibility: hidden;
                    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                    transform: translateY(20px) scale(0.8);
                }

                #btt-button.btt-visible {
                    opacity: 1;
                    visibility: visible;
                    transform: translateY(0) scale(1);
                }

                #btt-button:hover {
                    transform: translateY(-5px) scale(1.1);
                }

                #btt-button:active {
                    transform: translateY(-2px) scale(1.05);
                }

                /* 按钮样式 */
                #btt-button .btt-icon {
                    width: 100%;
                    height: 100%;
                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                    border-radius: 50%;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
                    transition: all 0.3s ease;
                }

                #btt-button:hover .btt-icon {
                    box-shadow: 0 6px 25px rgba(102, 126, 234, 0.6);
                }

                /* 箭头 SVG */
                #btt-button .btt-arrow {
                    width: 24px;
                    height: 24px;
                    fill: #ffffff;
                    transition: transform 0.3s ease;
                }

                #btt-button:hover .btt-arrow {
                    transform: translateY(-3px);
                }

                /* 进度环 */
                #btt-button .btt-progress {
                    position: absolute;
                    top: -3px;
                    left: -3px;
                    width: calc(100% + 6px);
                    height: calc(100% + 6px);
                    transform: rotate(-90deg);
                }

                #btt-button .btt-progress-circle {
                    fill: none;
                    stroke: rgba(102, 126, 234, 0.3);
                    stroke-width: 2;
                }

                #btt-button .btt-progress-bar {
                    fill: none;
                    stroke: #667eea;
                    stroke-width: 2;
                    stroke-linecap: round;
                    transition: stroke-dashoffset 0.1s linear;
                }

                /* 响应式设计 */
                @media (max-width: 768px) {
                    #btt-button {
                        bottom: 20px;
                        right: 20px;
                        width: 45px;
                        height: 45px;
                    }
                }
            `;

            GM_addStyle(css);
        },

        /**
         * 创建按钮 HTML
         */
        createButton: function() {
            var buttonHTML = `
                <div id="btt-button" role="button" aria-label="Back to top" tabindex="0">
                    <!-- 进度环 -->
                    <svg class="btt-progress" viewBox="0 0 ${this.config.buttonSize + 6} ${this.config.buttonSize + 6}">
                        <circle class="btt-progress-circle" 
                                cx="${(this.config.buttonSize + 6) / 2}" 
                                cy="${(this.config.buttonSize + 6) / 2}" 
                                r="${this.config.buttonSize / 2}">
                        </circle>
                        <circle class="btt-progress-bar" 
                                cx="${(this.config.buttonSize + 6) / 2}" 
                                cy="${(this.config.buttonSize + 6) / 2}" 
                                r="${this.config.buttonSize / 2}"
                                stroke-dasharray="0 999">
                        </circle>
                    </svg>
                    
                    <!-- 按钮主体 -->
                    <div class="btt-icon">
                        <svg class="btt-arrow" viewBox="0 0 24 24">
                            <path d="M7 14l5-5 5 5z"/>
                        </svg>
                    </div>
                </div>
            `;

            $('body').append(buttonHTML);
        },

        /**
         * 缓存 DOM 元素
         */
        cacheElements: function() {
            this.$elements = {
                button: $('#btt-button'),
                progressBar: $('#btt-button .btt-progress-bar'),
                window: $(window),
                document: $(document),
                html: $('html, body')
            };
        },

        /**
         * 绑定事件
         */
        bindEvents: function() {
            var self = this;

            // 点击返回顶部
            this.$elements.button.on('click.btt', function(e) {
                e.preventDefault();
                self.scrollToTop();
            });

            // 键盘支持(回车键)
            this.$elements.button.on('keydown.btt', function(e) {
                if (e.key === 'Enter' || e.keyCode === 13) {
                    e.preventDefault();
                    self.scrollToTop();
                }
            });

            // 滚动事件(使用节流)
            this.$elements.window.on('scroll.btt', function() {
                self.handleScroll();
            });

            // 页面尺寸变化
            this.$elements.window.on('resize.btt', function() {
                clearTimeout(self.state.scrollTimer);
                self.state.scrollTimer = setTimeout(function() {
                    self.checkScrollPosition();
                }, 150);
            });
        },

        /**
         * 处理滚动事件(节流)
         */
        handleScroll: function() {
            var self = this;

            clearTimeout(this.state.scrollTimer);
            this.state.scrollTimer = setTimeout(function() {
                self.checkScrollPosition();
                self.updateProgress();
            }, 100);
        },

        /**
         * 检查滚动位置并显示/隐藏按钮
         */
        checkScrollPosition: function() {
            var scrollTop = this.$elements.window.scrollTop();
            var shouldShow = scrollTop > this.config.scrollThreshold;

            if (shouldShow && !this.state.isVisible) {
                this.showButton();
            } else if (!shouldShow && this.state.isVisible) {
                this.hideButton();
            }
        },

        /**
         * 显示按钮
         */
        showButton: function() {
            var self = this;

            setTimeout(function() {
                self.$elements.button.addClass('btt-visible');
                self.state.isVisible = true;
            }, this.config.showDelay);
        },

        /**
         * 隐藏按钮
         */
        hideButton: function() {
            var self = this;

            setTimeout(function() {
                self.$elements.button.removeClass('btt-visible');
                self.state.isVisible = false;
            }, this.config.hideDelay);
        },

        /**
         * 更新滚动进度环
         */
        updateProgress: function() {
            var scrollTop = this.$elements.window.scrollTop();
            var docHeight = this.$elements.document.height();
            var winHeight = this.$elements.window.height();
            var scrollPercent = scrollTop / (docHeight - winHeight);
            var radius = this.config.buttonSize / 2;
            var circumference = 2 * Math.PI * radius;
            var offset = circumference - (scrollPercent * circumference);

            this.$elements.progressBar.css({
                'stroke-dasharray': circumference + ' 999',
                'stroke-dashoffset': offset
            });
        },

        /**
         * 滚动到顶部
         */
        scrollToTop: function() {
            var self = this;

            if (this.state.isAnimating) {
                return;
            }

            this.state.isAnimating = true;

            this.$elements.html.animate(
                { scrollTop: 0 },
                {
                    duration: this.config.animationDuration,
                    easing: 'swing',
                    complete: function() {
                        self.state.isAnimating = false;
                        
                        // 聚焦到页面顶部(无障碍支持)
                        $('body').attr('tabindex', '-1').focus();
                        setTimeout(function() {
                            $('body').removeAttr('tabindex');
                        }, 100);
                    }
                }
            );
        },

        /**
         * 销毁实例
         */
        destroy: function() {
            this.$elements.window.off('.btt');
            this.$elements.button.off('.btt').remove();
            clearTimeout(this.state.scrollTimer);
            console.log('[BackToTop] Destroyed');
        }
    };

    /**
     * 页面加载完成后初始化
     */
    $(document).ready(function() {
        // 等待页面完全加载
        setTimeout(function() {
            BackToTop.init();
        }, 500);
    });

    // 暴露到全局(方便调试)
    window.BackToTop = BackToTop;

})(jQuery);

主要特性:

专业特性

  1. 模块化设计 – 清晰的命名空间和职责分离
  2. 配置分离 – 所有参数可自定义
  3. 性能优化 – 滚动事件节流,DOM 查询缓存
  4. 优雅动画 – CSS3 动画 + jQuery 平滑滚动
  5. 响应式 – 移动端自适应

用户体验

  1. 滚动进度环 – 直观显示滚动进度
  2. 渐变背景 – 现代化设计
  3. 悬停效果 – 交互反馈
  4. 键盘支持 – 无障碍访问
  5. 智能显示 – 滚动 300px 后才出现

兼容性

  1. 全站点支持@match *://*/*
  2. 不干扰原网页 – 高 z-index 层级
  3. 移动端优化 – 响应式断点

使用方法:

  1. 安装 Tampermonkey
  2. 创建新脚本 – 粘贴以上代码
  3. 保存并启用
  4. 访问任意网页 – 自动生效

自定义配置:

javascript

// 修改配置项
config: {
    scrollThreshold: 500,        // 改为 500px 才显示
    buttonSize: 60,              // 改为 60px 大小
    bottomOffset: 50,            // 距离底部 50px
    // ... 其他配置
}

调试命令(浏览器控制台):

javascript

// 销毁按钮
BackToTop.destroy();

// 手动滚动到顶部
BackToTop.scrollToTop();

// 查看当前状态
console.log(BackToTop.state);

这是一个生产级别的油猴脚本,代码规范、功能完善、用户体验优秀!🚀

Last Updated:
版权声明:
本文标题:油猴脚本实现全网页通用的返回顶部功能
本文链接:https://haoyelaiga.com/%e6%b2%b9%e7%8c%b4%e8%84%9a%e6%9c%ac%e5%ae%9e%e7%8e%b0%e5%85%a8%e7%bd%91%e9%a1%b5%e9%80%9a%e7%94%a8%e7%9a%84%e8%bf%94%e5%9b%9e%e9%a1%b6%e9%83%a8%e5%8a%9f%e8%83%bd/
版权所有:好嘢
转载请保留出处,谢谢合作!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部