返回顶部和底部-美化版

一个很漂亮的可返回顶部或底部的可拖拽的按钮,支持忽略网站和右键菜单

当前为 2025-04-08 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         返回顶部和底部-美化版
// @version      1.1.4
// @description  一个很漂亮的可返回顶部或底部的可拖拽的按钮,支持忽略网站和右键菜单
// @author       沐雨
// @license      MIT
// @match        *://*/*
// @grant        GM_info
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_getResourceText
// @require      http://code.jquery.com/jquery-1.11.0.min.js
// @namespace    https://www.yuxi.com/
// ==/UserScript==

; (function () {
  'use strict'

  // 检查是否在iframe中运行
  if (window.self !== window.top) {
    // 在iframe中运行,不执行脚本
    return;
  }

  // 检查当前网站是否在忽略列表中
  const currentHost = window.location.hostname;
  const ignoredSites = JSON.parse(localStorage.getItem('btnScript_ignoredSites') || '[]');

  // 如果当前网站在忽略列表中,则不执行脚本
  if (ignoredSites.includes(currentHost)) {
    return;
  }

  var TBLink = function () {
    var $ = $ || window.$
    var $doc = $(document)
    var $win = $(window)
    // const iconFont = GM_getResourceText('css')
    var CreateHtml = function () {
      var html = `
      <div id="goTopBottom" style="display:flex;justify-content: center;align-items: center;width:50px;height:100px;">
                      <span class="t-btn gotop" title="返回顶部" style="opacity: 0;">
                        <svg t="1681901274619" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3044" id="mx_n_1681901274619" width="22" height="22"><path d="M199.36 572.768a31.904 31.904 0 0 0 22.624-9.376l294.144-294.144 285.728 285.728a31.968 31.968 0 1 0 45.248-45.248l-308.352-308.352a32 32 0 0 0-45.28 0l-316.768 316.768a31.968 31.968 0 0 0 22.656 54.624z" p-id="3045" fill="#ffffff"></path><path d="M538.784 457.376a32 32 0 0 0-45.28 0l-316.768 316.768a31.968 31.968 0 1 0 45.248 45.248l294.144-294.144 285.728 285.728a31.968 31.968 0 1 0 45.248-45.248l-308.32-308.352z" p-id="3046" fill="#ffffff"></path></svg>
                      </span>

                      <span class="t-btn bottom" title="返回底部" style="opacity: 0;">
                         <svg t="1681901383895" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4902" id="mx_n_1681901383896" width="22" height="22"><path d="M493.504 558.144a31.904 31.904 0 0 0 45.28 0l308.352-308.352a31.968 31.968 0 1 0-45.248-45.248l-285.728 285.728-294.176-294.144a31.968 31.968 0 1 0-45.248 45.248l316.768 316.768z" p-id="4903" fill="#ffffff"></path><path d="M801.888 460.576l-285.728 285.728-294.144-294.144a31.968 31.968 0 1 0-45.248 45.248l316.768 316.768a31.904 31.904 0 0 0 45.28 0l308.352-308.352a32 32 0 1 0-45.28-45.248z" p-id="4904" fill="#ffffff"></path></svg>
                      </span>
                </div>
`
      $('html body').append(html)

      // 添加右键菜单
      $('#goTopBottom').on('contextmenu', showContextMenu);
    }

    // 创建右键菜单
    var createContextMenu = function() {
      var menu = $('<div id="gtb-context-menu"></div>');
      menu.css({
        position: 'fixed',
        background: 'white',
        border: '1px solid #ddd',
        borderRadius: '8px',
        boxShadow: '0 3px 10px rgba(0,0,0,0.15)',
        padding: '5px 0',
        zIndex: 10000000,
        display: 'none',
        width: '140px',
        boxSizing: 'border-box',
        maxWidth: '140px',
        overflow: 'hidden',
        fontFamily: 'Arial, sans-serif',
        fontSize: '14px',
        transition: 'opacity 0.2s ease'
      });

      // 创建一个菜单项
      function createMenuItem(text, icon, onClick) {
        var item = $('<div></div>');
        item.css({
          padding: '8px 12px',
          cursor: 'pointer',
          borderRadius: '4px',
          margin: '2px 5px',
          whiteSpace: 'nowrap',
          overflow: 'hidden',
          textOverflow: 'ellipsis',
          transition: 'background-color 0.2s',
          display: 'flex',
          alignItems: 'center',
          gap: '5px'
        });

        // 创建图标元素
        var iconElem = $('<div></div>');
        iconElem.css({
          flexShrink: 0,
          width: '14px',
          height: '14px',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          fontSize: '12px'
        });
        iconElem.text(icon);

        // 创建文本元素
        var textElem = $('<div></div>');
        textElem.css({
          flexGrow: 1,
          overflow: 'hidden',
          textOverflow: 'ellipsis'
        });
        textElem.text(text);

        // 组装
        item.append(iconElem).append(textElem);

        // 添加悬停效果
        item.hover(
          function() { $(this).css('backgroundColor', '#f2f2f2'); },
          function() { $(this).css('backgroundColor', 'transparent'); }
        );

        // 添加点击事件
        item.on('click', onClick);

        return item;
      }

      // 创建菜单项
      var closeOption = createMenuItem('关闭按钮', '✕', hideScrollButtons);
      var ignoreOption = createMenuItem('忽略此网站', '⛔', ignoreCurrentSite);

      menu.append(closeOption);
      menu.append(ignoreOption);
      $('body').append(menu);

      return menu;
    }

    // 显示右键菜单
    var showContextMenu = function(e) {
      e.preventDefault();

      var menu = $('#gtb-context-menu');
      if (menu.length === 0) {
        menu = createContextMenu();
      }

      // 使用更可靠的方式获取视口宽度和高度
      var viewportWidth = Math.min(
        document.documentElement.clientWidth,
        window.innerWidth || 0
      );
      var viewportHeight = Math.min(
        document.documentElement.clientHeight,
        window.innerHeight || 0
      );

      // 设置固定宽度,防止内容撑开
      menu.css({
        width: '140px',
        visibility: 'hidden',
        display: 'block',
        left: 0,  // 临时位置,只用于获取高度
        top: 0
      });

      var menuHeight = menu.outerHeight(true);

      // 计算最佳位置
      var posX, posY;

      // 水平位置计算 - 优先放在点击位置,如果靠近右边界则显示在左侧
      if (e.clientX + 160 > viewportWidth) { // 增加余量,确保不触发滚动条
        posX = Math.max(e.clientX - 150, 10); // 放在左侧,确保至少有10px边距
      } else {
        posX = e.clientX;
      }

      // 垂直位置计算 - 保持在可视区域内
      if (e.clientY + menuHeight + 20 > viewportHeight) {
        posY = viewportHeight - menuHeight - 20; // 确保底部有20px边距
      } else {
        posY = e.clientY;
      }

      // 设置菜单最终位置并显示
      menu.css({
        left: posX + 'px',
        top: posY + 'px',
        visibility: 'visible'
      });

      // 点击其他区域关闭菜单
      setTimeout(function() {
        $(document).one('click', function() {
          menu.hide();
        });
      }, 100);
    }

    // 隐藏滚动按钮
    var hideScrollButtons = function() {
      $('#goTopBottom').hide();
    }

    // 忽略当前网站
    var ignoreCurrentSite = function() {
      var currentHost = window.location.hostname;
      var ignoredSites = JSON.parse(localStorage.getItem('btnScript_ignoredSites') || '[]');

      // 添加当前网站到忽略列表
      if (!ignoredSites.includes(currentHost)) {
        ignoredSites.push(currentHost);
        localStorage.setItem('btnScript_ignoredSites', JSON.stringify(ignoredSites));
      }

      // 隐藏按钮
      hideScrollButtons();
    }

    // 用于存储最后拖拽结束的时间戳
    var lastDragEndTime = 0;
    // 拖拽后多长时间内不触发点击(毫秒)
    var clickPreventionDelay = 500;

    // 检查是否应该阻止点击
    var shouldPreventClick = function() {
      return Date.now() - lastDragEndTime < clickPreventionDelay;
    }

    var CreateStyle = function () {
      var style =
        '#goTopBottom {position: fixed;bottom: 75px;right: 15px;z-index: 999999;display: flex;flex-direction: column;row-gap: 5px; opacity: 0.7; transition: opacity 0.3s;}' +
        '#goTopBottom:hover {opacity: 1;}' +
        '#goTopBottom .top {opacity: 0;}' +
        '#goTopBottom .t-btn {display: flex;justify-content: center;align-items: center;width: 40px;height: 40px;cursor: pointer;color: #fff;border-radius: 4px;background-image: linear-gradient(to top right,#6966ff,#37e2d3);background-size: 100% 100%;background-color: transparent;}' +
        '#goTopBottom .bottom {opacity: 0;}'
      // GM_addStyle(iconFont)
      GM_addStyle(style)
    }
    var GoTB = function () {
      var upperLimit = 100
      var scrollSpeed = 500
      var fadeSpeed = 300
      var $top = $('#goTopBottom .gotop')
      var $bottom = $('#goTopBottom .bottom')
      if (getScrollTop() > upperLimit) {
        $top.stop().fadeTo(0, 1, function () {
          $top.show()
        })
      }
      if (getScrollTop() + $(window).height() < $doc.height() - upperLimit) {
        $bottom.stop().fadeTo(0, 1, function () {
          $bottom.show()
        })
      }
      $doc.scroll(function () {
        if (getScrollTop() > upperLimit) {
          $top.stop().fadeTo(fadeSpeed, 1, function () {
            $top.css('visibility', 'visible')
          })
        } else {
          $top.stop().fadeTo(fadeSpeed, 0, function () {
            $top.css('visibility', 'hidden')
          })
        }
        if (getScrollTop() + $(window).height() < $doc.height() - upperLimit) {
          $bottom.stop().fadeTo(fadeSpeed, 1, function () {
            $bottom.css('visibility', 'visible')
          })
        } else {
          $bottom.stop().fadeTo(fadeSpeed, 0, function () {
            $bottom.css('visibility', 'hidden')
          })
        }
      })

      // 移除旧的点击处理器,确保不会重复绑定
      $('#goTopBottom span').off('click');

      // 添加新的点击处理器
      $('#goTopBottom span').on('click', function (e) {
        // 如果刚拖拽完成,阻止点击
        if (shouldPreventClick()) {
          e.preventDefault();
          e.stopPropagation();
          return false;
        }

        var $this = $(this)
        var clsName = $this.attr('class')

        // 改进的滚动处理,处理懒加载内容
        if (clsName.includes('gotop')) {
          // 滚动到顶部
          $('html, body').animate({
            scrollTop: 0
          }, scrollSpeed)
        } else {
          // 滚动到底部,处理懒加载内容
          const maxScrollHeight = Math.max(
            document.body.scrollHeight,
            document.body.offsetHeight,
            document.documentElement.clientHeight,
            document.documentElement.scrollHeight,
            document.documentElement.offsetHeight
          );

          $('html, body').animate({
            scrollTop: maxScrollHeight
          }, scrollSpeed)
        }

        return false
      })
    }
    var getScrollTop = function () {
      var scrollTop = $doc.scrollTop() || $('html,body').scrollTop()
      return scrollTop
    }
    /**
     * 拖拽
     */
    function dragging() {
      let isDragging = false;
      let dragThreshold = 5; // 拖拽阈值,移动超过这个距离才算拖拽

      var position = GM_getValue('gtb_pos') || {}
      var $gtbBox = $('#goTopBottom');

      $('#goTopBottom')
        .off('.gtb')
        .on({
          'mousedown.gtb': function (el) {
            if (el.button !== 0) return; // 只在左键点击时处理

            var move = true
            var startX = el.pageX
            var startY = el.pageY
            var startLeft = $gtbBox.offset().left
            var startTop = $gtbBox.offset().top
            var movedDistance = 0

            $doc.on({
              'mousemove.gtb': function (docEl) {
                if (move) {
                  var deltaX = docEl.pageX - startX
                  var deltaY = docEl.pageY - startY
                  movedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)

                  // 如果移动距离超过阈值,则认为是拖拽
                  if (movedDistance > dragThreshold) {
                    isDragging = true;
                    $gtbBox.offset({
                      left: startLeft + deltaX,
                      top: startTop + deltaY
                    })
                  }
                }
              },
              'mouseup.gtb': function () {
                move = false
                $doc.off('.gtb')

                // 如果是拖拽操作,记录结束时间
                if (isDragging) {
                  lastDragEndTime = Date.now();
                  isDragging = false;
                }
              }
            })
          }
        });
    }
    this.init = function () {
      CreateStyle()
      CreateHtml()
      dragging()
      GoTB()
    }
  }
  var tbl = new TBLink()
  tbl.init()
})()