Greasy Fork

返回顶部和底部-美化版

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

目前为 2025-04-08 提交的版本。查看 最新版本

// ==UserScript==
// @name         返回顶部和底部-美化版
// @version      1.1.2
// @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: 'absolute',
        background: 'white',
        border: '1px solid #ddd',
        borderRadius: '8px',
        boxShadow: '0 3px 10px rgba(0,0,0,0.15)',
        padding: '5px 0',
        zIndex: 10000,
        display: 'none',
        minWidth: '140px',
        fontFamily: 'Arial, sans-serif',
        fontSize: '14px',
        transition: 'opacity 0.2s ease'
      });

      // 创建关闭选项
      var closeOption = $('<div>关闭按钮</div>');
      closeOption.css({
        padding: '10px 15px',
        cursor: 'pointer',
        borderRadius: '4px',
        margin: '2px 5px',
        transition: 'background-color 0.2s'
      });
      closeOption.hover(
        function() { $(this).css('backgroundColor', '#f2f2f2'); },
        function() { $(this).css('backgroundColor', 'transparent'); }
      );
      closeOption.on('click', hideScrollButtons);

      // 创建忽略网站选项
      var ignoreOption = $('<div>忽略此网站</div>');
      ignoreOption.css({
        padding: '10px 15px',
        cursor: 'pointer',
        borderRadius: '4px',
        margin: '2px 5px',
        transition: 'background-color 0.2s'
      });
      ignoreOption.hover(
        function() { $(this).css('backgroundColor', '#f2f2f2'); },
        function() { $(this).css('backgroundColor', 'transparent'); }
      );
      ignoreOption.on('click', ignoreCurrentSite);

      // 添加图标
      var closeIcon = '<span style="margin-right:8px;display:inline-block;width:14px;">✕</span>';
      var ignoreIcon = '<span style="margin-right:8px;display:inline-block;width:14px;">⛔</span>';

      closeOption.html(closeIcon + '关闭按钮');
      ignoreOption.html(ignoreIcon + '忽略此网站');

      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 windowWidth = window.innerWidth;
      var windowHeight = window.innerHeight;

      // 先显示菜单但设置为不可见,以便计算尺寸
      menu.css({
        visibility: 'hidden',
        display: 'block'
      });

      var menuWidth = menu.outerWidth();
      var menuHeight = menu.outerHeight();

      // 计算最佳位置,防止菜单溢出窗口
      var posX = e.clientX;
      var posY = e.clientY;

      // 右侧边界检查
      if (posX + menuWidth > windowWidth - 10) {
        posX = windowWidth - menuWidth - 10;
      }

      // 底部边界检查
      if (posY + menuHeight > windowHeight - 10) {
        posY = windowHeight - menuHeight - 10;
      }

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

      // 点击其他区域关闭菜单
      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()
})()