HeiGoBackTop.js

可能是最漂亮的返回顶部插件。可以用来返回页面顶部,或者跳转底部,也可以用来自动化滑动页面。已经开源于github。

Verze ze dne 15. 09. 2024. Zobrazit nejnovější verzi.

Tento skript by neměl být instalován přímo. Jedná se o knihovnu, kterou by měly jiné skripty využívat pomocí meta příkazu // @require https://update.greatest.deepsurf.us/scripts/426407/1448116/HeiGoBackTopjs.js

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

/*
 * HeiGoBackTop.js V1.0.2
 * @author hei-jack
 * @link https://github.com/hei-jack/HeiGoBackTop/
 * Probably the most beautiful back to top widget
 * 可能是最漂亮的返回顶部小插件
 * first: GMT2021-07-22
 * update: GMT2024-09-15
 *
 */
; (function (global) {
  "use strict";
  //构造函数
  function HeiGoBackTop(mode, el, speed, distance, smooth) {
    this.mode = mode === undefined ? 0 : mode;
    this.el = el === undefined ? '#__go-back-top' : el;
    this.speed = speed === undefined ? 500 : speed;
    this.distance = distance === undefined ? 100 : distance;
    this.smooth = smooth === undefined ? true : smooth;
    this.show_height = 400;
    this.flag = false;
    this.show_flag = false;
    this.width = '150px';
    this.height = '40px';
    this.top = '90%';
    this.right = '5%';
    this.text = '返回顶部';
    this.text_color = '#fff';
    this.radius = '40px';
    this.themes = 0;
    this.color = 'linear-gradient(to right,#6966ff,#37e2d3,#63e8dd,#ccff66)';
    this.shadow = '0 4px 15px 0 rgba(41, 163, 163,0.75)';
    this.version = 'V1.0.2';
    this.home = 'https://github.com/hei-jack/HeiGoBackTop/';
    //页面加载结束才进行初始化 没有必要执行实时载入
    this.onLoad(this);
  };

  //拓展方法
  HeiGoBackTop.prototype = {
    constructor: HeiGoBackTop,
    //钩子回调
    hook: function (func) {
      return func === undefined ? false : this.callBack(func.bind(this));
    },
    //beforeCreate 初始化之前执行
    onBeforeCreate: function (func) {
      this.beforeCreate = func;
    },
    //初始化结束事件
    onAfterCreate: function (func) {
      this.afterCreate = func;
    },
    onShow: function (func) {
      this.show = func;
    },
    onHide: function (func) {
      this.hide = func;
    },
    onClick: function (func) {
      this.click = func;
    },
    //回调
    callBack: function (func) {
      return func();
    },
    //初始化方法
    init: function () {
      this.showVersion();
      this.hook(this.beforeCreate);
      this.unset('onBeforeCreate', null);
      this.unset('beforeCreate');
      //检查参数合法性
      if (!this.checkArgs()) throw (new Error('arguments is error!'));
      this.unset('checkNum', null);
      this.unset('checkEl', null);
      this.unset('checkArgs', null);
      //初始化按钮
      this.createBtn();
      this.unset('createBtn', null);
      this.unset('unsetUseless', null);
      //绑定自定义鼠标滑动事件到全局滑动事件
      this.bindOn(window, 'scroll', this.checkBtn);
      this.bindOn(window, 'scroll', this.scroll);
      this.controller();
      this.unset('controller', null);
      this.hook(this.afterCreate);
      this.unset('onAfterCreate', null);
      this.unset('afterCreate');
      this.unset('bindOn', null);
      this.unset('addEventListener', null);
      this.unset('isBrowser', null);
      this.unset('onLoad', null)
    },
    //检查参数
    checkArgs: function () {
      if (this.checkNum(this.mode)) return false;
      if (this.mode > 3 || this.mode < 0) return false;
      if (this.checkNum(this.speed) || this.speed <= 0) return false;
      if (this.checkNum(this.distance) || this.distance <= 0) return false;
      if (this.checkEl(this.el)) return false;
      return true
    },
    //检查参数类型是否为数字
    checkNum: function (arg) {
      return typeof (arg) !== 'number';
    },
    //检查参数类型是否为不为0 且包含#号的字符串
    checkEl: function (arg) {
      return typeof (arg) !== 'string' || arg.length === 0 || arg.indexOf('#') === -1;
    },
    //创建按钮
    createBtn: function () {
      //如果不是初始元素id 说明用户自定义挂载元素
      if (this.el !== '#__go-back-top') {
        this.el = this.el.replace('#', '');
        if (document.getElementById(this.el)) {
          this.btn = document.getElementById(this.el);
          this.unsetUseless();
          return false
        }
        //挂载元素id没有发现 直接抛出错误
        throw (new Error('element is error!'));
      }
      var style_text = '#__go-back-top{display:none;position:fixed;width:' + this.width + ';height:' + this.height + ';top:' + this.top + ';right:' + this.right + ';z-index:9999;}.__go-back-btn{width:' + this.width + ';height:' + this.height + ';background:' + this.color + ';background-size: 300% 100%;cursor: pointer;border:none;border-radius:' + this.radius + ';box-shadow:' + this.shadow + ';color:' + this.text_color + ';outline:none;letter-spacing:2px;font-weight:600;font-family: "YouYuan","Microsoft YaHei","SimHei","SimSun","Arial",sans-serif;transition: all .4s ease-in-out;moz-transition: all .4s ease-in-out;-o-transition: all .4s ease-in-out;-webkit-transition: all .4s ease-in-out;}.__go-back-btn:hover{background-position: 100% 0%;moz-transition: all .4s ease-in-out;-o-transition: all .4s ease-in-out;-webkit-transition: all .4s ease-in-out;transition: all .4s ease-in-out;}.__go-back-btn:focus{border:none;outline:none;}.go-back-top-themes1{background-image:linear-gradient(to right,#25aae1,#40e495,#30dd8a,#2bb673);box-shadow:0 4px 15px 0 rgba(49,196,190,0.75);}.go-back-top-themes2{background-image:linear-gradient(to right,#f5ce62,#e43603,#fa7199,#e85a19);box-shadow:0 4px 15px 0 rgba(229,66,10,0.75);}.go-back-top-themes3{background-image:linear-gradient(to right,#667eea,#764ba2,#6B8DD6,#8E37D7);box-shadow:0 4px 15px 0 rgba(116,79,168,0.75);}.go-back-top-themes4{background-image:linear-gradient(to right,#fc6076,#ff9a44,#ef9d43,#e75516);box-shadow:0 4px 15px 0 rgba(252,104,110,0.75);}.go-back-top-themes5{background-image:linear-gradient(to right,#0ba360,#3cba92,#30dd8a,#2bb673);box-shadow:0 4px 15px 0 rgba(23,168,108,0.75);}.go-back-top-themes6{background-image:linear-gradient(to right,#009245,#FCEE21,#00A8C5,#D9E021);box-shadow:0 4px 15px 0 rgba(83,176,57,0.75);}.go-back-top-themes7{background-image:linear-gradient(to right,#6253e1,#852D91,#A3A1FF,#F24645);box-shadow:0 4px 15px 0 rgba(126,52,161,0.75);}.go-back-top-themes8{background-image:linear-gradient(to right,#29323c,#485563,#2b5876,#4e4376);box-shadow:0 4px 15px 0 rgba(45,54,65,0.75);}.go-back-top-themes9{background-image:linear-gradient(to right,#25aae1,#4481eb,#04befe,#3f86ed);box-shadow:0 4px 15px 0 rgba(65,132,234,0.75);}.go-back-top-themes10{background-image:linear-gradient(to right,#ed6ea0,#ec8c69,#f7186a,#FBB03B);box-shadow:0 4px 15px 0 rgba(236,116,149,0.75);}.go-back-top-themes11{background-image:linear-gradient(to right,#eb3941,#f15e64,#e14e53,#e2373f);box-shadow:0 5px 15px rgba(242,97,103,.4);}';
      var style_el = document.createElement('style');
      var style_node = document.createTextNode(style_text);
      style_el.appendChild(style_node);
      document.body.appendChild(style_el);
      var div = document.createElement('div');
      div.setAttribute('id', this.el.replace('#', ''));
      var btn = document.createElement('button');
      //设置主题  为0是默认主题 只有默认主题允许更改样式
      this.themes === 0 ? btn.setAttribute('class', this.el.replace('#', '').replace('top', 'btn')) : btn.setAttribute('class', this.el.replace('#', '').replace('top', 'btn') + ' go-back-top-themes' + this.themes);
      var btn_name = document.createTextNode(this.text);
      btn.appendChild(btn_name);
      div.appendChild(btn);
      document.body.appendChild(div);
      this.btn = div;
      //创建结束 销毁已经没用的属性
      this.unsetUseless();
    },
    showBtn: function () {
      if (!this.show_flag) {
        this.btn.style.cssText = 'display:block';
        this.hook(this.show);
        this.show_flag = true;
      }
    },
    hideBtn: function () {
      if (this.show_flag) {
        this.btn.style.cssText = 'display:none';
        this.hook(this.hide);
        this.show_flag = false;
      }
    },
    //载入方法 在页面加载结束后开始执行初始化工作
    onLoad: function (self) {
      if (!this.isBrowser()) throw (new Error('The current environment is not the browser!'));
      if (this.isIE() && this.getIEVersion() < 9) throw (new Error('HeiGoBackTop does not support ie 9 the following browsers!'));
      this.bindOn(window, 'load', self.init);
    },
    //当滑动事件发生时
    onScroll: function (func) {
      this.scroll = func;
    },
    //绑定元素事件
    bindOn: function (el, event, func) {
      if (func === undefined) return false;
      this.addEventListener(el, event, func.bind(this))
    },
    //获取页面滚动的距离
    getScrollTop: function () {
      return document.documentElement.scrollTop || document.body.scrollTop;
    },
    //检查按钮何时显示和隐藏
    checkBtn: function () {
      this.getScrollTop() > this.show_height ? this.showBtn() : this.hideBtn();
    },
    //绑定元素事件兼容处理函数
    addEventListener: function (el, type, fn) {
      if (el.addEventListener) {
        el.addEventListener(type, fn, false);
      } else if (el.attachEvent) {
        el.attachEvent('on' + type, fn);
      } else {
        return false;
      }
    },
    //展示版本信息
    showVersion: function () {
      this.isIE() ? console.log("HeiGoBackTop " + this.version + ' ' + this.home) : console.log("\n\n %c HeiGoBackTop " + this.version + " %c " + this.home + " \n\n", "color: #fff; background: linear-gradient(90deg, #8080ff, #ff99ff); padding:5px 1px;", "background: linear-gradient(90deg,#ffccff,#80ffd4); padding:5px 0px;")
    },
    //模式分发控制器
    controller: function () {
      switch (this.mode) {
        case 0:
          this.bindOn(this.btn, 'click', this.goBackTop);
          break;
        case 1:
          this.bindOn(this.btn, 'click', this.goDown);
          break;
        case 2:
          this.bindOn(this.btn, 'click', this.goBackTopSlow);
          break;
        case 3:
          this.bindOn(this.btn, 'click', this.goDownSlow)
      }
    },
    //返回顶部
    goBackTop: function () {
      // 如果开启平滑模式
      if (this.smooth) {
        this.scrollToSmooth(0);
      } else {
        this.setScrollTop(0);
      }
      //滑动结束钩子
      this.hook(this.scrollOver);
    },
    //慢滑到顶部
    goBackTopSlow: function () {
      this.scrollSpeed(this.getScrollTop(), 0);
    },
    //跳转底部
    goDown: function () {
      // 如果开启平滑模式
      if (this.smooth) {
        this.scrollToSmooth(this.getScrollHeight());
      } else {
        this.setScrollTop(this.getScrollHeight());
      }
      this.hook(this.scrollOver)
    },
    //慢滑到底部
    goDownSlow: function () {
      this.scrollSpeed(this.getScrollTop(), this.getScrollHeight());
    },
    /*
     * 滑动方法
     * @param number start 开始滑动位置
     * @param number end 结束滑动位置
     * 
     */
    scrollSpeed: function (start, end) {
      //防止重复点击导致定时器多次调用
      if (this.flag) return false;
      this.flag = true;
      var timer = null;
      if (end === 0) {
        //如果是滑动到顶部
        timer = setInterval(function () {
          var scroll_top = this.getScrollTop();
          if (scroll_top >= this.distance) {
            start -= this.distance;
            this.setScrollTop(start);
          } else {
            if (scroll_top === 0) {
              this.flag = false;
              clearInterval(timer);
              this.hook(this.scrollOver);
            } else {
              this.setScrollTop(0);
            }
          }
        }.bind(this), this.speed);
      } else {
        //如果是滑动到底部
        timer = setInterval(function () {
          //getLast方法获取滚动条距离底部还剩多少距离 如果大于0 说明未到达底部 只管继续滑动即可
          //向下取整 修复个别浏览器还剩余0.3左右的问题
          if (Math.floor(this.getLast()) > 0) {
            start += this.distance;
            this.setScrollTop(start);
          } else {
            this.flag = false;
            this.setScrollTop(end);
            clearInterval(timer);
            this.hook(this.scrollOver);
          }
        }.bind(this), this.speed);
      }
    },
    // 平滑的滚动
    scrollToSmooth: function (target) {
      //  IE和safari不支持 options.behavior:"smooth"
      // 目标位置
      var targetPosition = target;
      // 开始滚动位置
      var startPosition = this.getScrollTop();

      try {
        //如果出现异常 说明不支持behavior: 'smooth'
        global.scrollTo({
          top: targetPosition,
          behavior: 'smooth'
        });
      } catch (e) {
        var distance = targetPosition - startPosition;
        var duration = 500; // 滚动持续时间,单位为毫秒
        var startTime = null;

        function animation(currentTime) {
          if (startTime === null) startTime = currentTime;
          var timeElapsed = currentTime - startTime;
          var run = ease(timeElapsed, startPosition, distance, duration);
          global.scrollTo(0, run);
          if (timeElapsed < duration) global.requestAnimationFrame(animation);
        }

        function ease(t, b, c, d) {
          t /= d / 2;
          if (t < 1) return c / 2 * t * t + b;
          t--;
          return -c / 2 * (t * (t - 2) - 1) + b;
        }

        global.requestAnimationFrame(animation);
      }

    },
    
    //设置当前滚动所在高度
    setScrollTop: function (height) {
      //处理兼容性问题
      document.documentElement.scrollTop ? document.documentElement.scrollTop = height : document.body.scrollTop = height;
    },
    //获取滚动条高度 即可滚动的高度
    getScrollHeight: function () {
      //兼容标准模式 strict mode 和 混杂模式 quirks mode
      return document.compatMode === 'CSS1Compat' ? document.documentElement.scrollHeight : document.body.scrollHeight;
    },
    /*
     * 销毁属性和方法
     * @param string key 属性或方法名称
     * @param set 可选参数 传入除undefined任意值则说明是方法 不传表示属性
     */
    unset: function (key, set) {
      if (set === undefined) {
        delete this[key];
      } else {
        //ie不支持delete方法
        this.isIE() && this.getIEVersion() <= 10 ? this[key] = undefined : this.__proto__[key] = undefined;
      }
    },
    unsetUseless: function () {
      this.unset('el');
      this.unset('width');
      this.unset('height');
      this.unset('top');
      this.unset('right');
      this.unset('text');
      this.unset('text_color');
      this.unset('radius');
      this.unset('themes');
      this.unset('color');
      this.unset('shadow')
    },
    //获取当前滚动条所在位置到页面底部还剩多少距离
    getLast: function () {
      var margin_bot = 0;
      if (document.compatMode === "CSS1Compat") {
        margin_bot = document.documentElement.scrollHeight - (document.documentElement.scrollTop + document.body.scrollTop) - document.documentElement.clientHeight;
      } else {
        margin_bot = document.body.scrollHeight - document.body.scrollTop - document.body.clientHeight;
      }
      return margin_bot;
    },
    //滑动结束执行事件
    onScrollOver: function (func) {
      this.scrollOver = func;
    },
    //判断当前浏览器是否为safari
    isSafari: function () {
      return (/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent));
    },
    //获取当前浏览器是否为ie
    isIE: function () {
      if (!!window.ActiveXObject || "ActiveXObject" in window) return true;
      return false;
    },
    //获取当前ie版本号
    getIEVersion: function () {
      var ua = navigator.userAgent;
      //如果不是ie浏览器 设置初始值0
      var ver = 0;
      if (ua) {
        if (ua.match(/MSIE\s+([\d]+)\./i)) {
          //其他ie版本
          ver = RegExp.$1;
        } else if (ua.match(/Trident.*rv\s*:\s*([\d]+)\./i)) {
          //ie11
          ver = RegExp.$1;
        }
      }
      return parseInt(ver);
    },
    //获取当前运行环境是否为浏览器
    isBrowser: function () {
      return typeof (window) === "undefined" ? false : true;
    }
  };
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = HeiGoBackTop;
  } else if (typeof define === 'function') {
    define(function () {
      return HeiGoBackTop;
    })
  } else {
    global.HeiGoBackTop = HeiGoBackTop;
  }
})(this);