Vue 2.x折腾记 - (6) 写一个不大靠谱的backToTop组件

简介: 返回顶部这个功能用jq实现,好容易实现,一个animate配合scrollTo就搞定了今天我们来试试vue封装一个原生js实现的返回顶部;写起来够呛,借助github,看了别人的gist,稍微封装了下;当然不是用scrollTo直接调位那种,没有过渡效果怎么说得过去,还是捣鼓出来了。


前言


返回顶部这个功能用jq实现,好容易实现,一个animate配合scrollTo就搞定了

今天我们来试试vue封装一个原生js实现的返回顶部;


写起来够呛,借助github,看了别人的gist,稍微封装了下;


当然不是用scrollTo直接调位那种,没有过渡效果怎么说得过去,还是捣鼓出来了。


效果图




实现思路


  • 过渡用的是requestAnimationFrame,这货只支持IE10+,所以必须做兼容
  • 滚动视图是window.pageYOffset,这货支持IE9+;
  • 为了让可控性更强,图标采用iconfont,具体瞅代码


你能学到什么?


  • 学到一些页面计算相关的东东
  • 动画API的一些知识
  • Vue封装组件相关知识和生命周期和事件监听销毁相关知识的运用


实现功能


  • 视图默认在350处显示返回顶部的按钮和图标
  • 提示文字和颜色,在图标上下左右的自定义,字段都限制了格式和默认值
  • 图标颜色和形状,大小的自定义,字段都限制了格式和默认值
  • 过渡动效的自定义,用法:scrollIt(0, 1500, 'easeInOutCubic', callback);


  • 返回到视图的point,也就是滚动到哪里
  • 过渡时间(ms级别)
  • 一堆过渡效果,字符串格式,其实就是滚动的计算函数..
  • 当然少不了默认参数了,除了callback


  • 兼容性是IE9+,特意开了虚拟机去尝试


代码


scrollIt.js --过渡滚动实现


export function scrollIt(
  destination = 0,
  duration = 200,
  easing = "linear",
  callback
) {
  // define timing functions -- 过渡动效
  let easings = {
    // no easing, no acceleration
    linear(t) {
      return t;
    },
    // accelerating from zero velocity
    easeInQuad(t) {
      return t * t;
    },
    // decelerating to zero velocity
    easeOutQuad(t) {
      return t * (2 - t);
    },
    // acceleration until halfway, then deceleration
    easeInOutQuad(t) {
      return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    },
    // accelerating from zero velocity
    easeInCubic(t) {
      return t * t * t;
    },
    // decelerating to zero velocity
    easeOutCubic(t) {
      return --t * t * t + 1;
    },
    // acceleration until halfway, then deceleration
    easeInOutCubic(t) {
      return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
    },
    // accelerating from zero velocity
    easeInQuart(t) {
      return t * t * t * t;
    },
    // decelerating to zero velocity
    easeOutQuart(t) {
      return 1 - --t * t * t * t;
    },
    // acceleration until halfway, then deceleration
    easeInOutQuart(t) {
      return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
    },
    // accelerating from zero velocity
    easeInQuint(t) {
      return t * t * t * t * t;
    },
    // decelerating to zero velocity
    easeOutQuint(t) {
      return 1 + --t * t * t * t * t;
    },
    // acceleration until halfway, then deceleration
    easeInOutQuint(t) {
      return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
    }
  };
  // requestAnimationFrame()的兼容性封装:先判断是否原生支持各种带前缀的
  //不行的话就采用延时的方案
  (function() {
    var lastTime = 0;
    var vendors = ["ms", "moz", "webkit", "o"];
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
      window.requestAnimationFrame =
        window[vendors[x] + "RequestAnimationFrame"];
      window.cancelAnimationFrame =
        window[vendors[x] + "CancelAnimationFrame"] ||
        window[vendors[x] + "CancelRequestAnimationFrame"];
    }
    if (!window.requestAnimationFrame)
      window.requestAnimationFrame = function(callback, element) {
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0, 16 - (currTime - lastTime));
        var id = window.setTimeout(function() {
          callback(currTime + timeToCall);
        }, timeToCall);
        lastTime = currTime + timeToCall;
        return id;
      };
    if (!window.cancelAnimationFrame)
      window.cancelAnimationFrame = function(id) {
        clearTimeout(id);
      };
  })();
  function checkElement() {
    // chrome,safari及一些浏览器对于documentElemnt的计算标准化,reset的作用
    document.documentElement.scrollTop += 1;
    let elm =
      document.documentElement.scrollTop !== 0
        ? document.documentElement
        : document.body;
    document.documentElement.scrollTop -= 1;
    return elm;
  }
  let element = checkElement(); 
  let start = element.scrollTop; // 当前滚动距离
  let startTime = Date.now(); // 当前时间
  function scroll() { // 滚动的实现
    let now = Date.now();
    let time = Math.min(1, (now - startTime) / duration);
    let timeFunction = easings[easing](time);
    element.scrollTop = timeFunction * (destination - start) + start;
    if (element.scrollTop === destination) {
      callback; // 此次执行回调函数
      return;
    }
    window.requestAnimationFrame(scroll);
  }
  scroll();
}


backToTop.vue


<template>
  <div class="back-to-top" @click="backToTop" v-show="showReturnToTop" @mouseenter="show" @mouseleave="hide">
    <i :class="[bttOption.iClass]" :style="{color:bttOption.iColor,'font-size':bttOption.iFontsize}"></i>
    <span class="tips" :class="[bttOption.iPos]" :style="{color:bttOption.textColor}" v-show="showTooltips">{{bttOption.text}}</span>
  </div>
</template>
<script>
  import { scrollIt } from './scrollIt'; // 引入动画过渡的实现
  export default {
    name: 'back-to-top',
    props: {
      text: { // 文本提示
        type: String,
        default: '返回顶部'
      },
      textColor: { // 文本颜色
        type: String,
        default: '#f00'
      },
      iPos: { // 文本位置
        type: String,
        default: 'right'
      },
      iClass: { // 图标形状
        type: String,
        default: 'fzicon fz-ad-fanhuidingbu1'
      },
      iColor: { // 图标颜色
        type: String,
        default: '#f00'
      },
      iFontsize: { // 图标大小
        type: String,
        default: '32px'
      },
      pageY: { // 默认在哪个视图显示返回按钮
        type: Number,
        default: 400
      },
      transitionName: { // 过渡动画名称
        type: String,
        default: 'linear'
      }
    },
    data: function () {
      return {
        showTooltips: false,
        showReturnToTop: false
      }
    },
    computed: {
      bttOption () {
        return {
          text: this.text,
          textColor: this.textColor,
          iPos: this.iPos,
          iClass: this.iClass,
          iColor: this.iColor,
          iFontsize: this.iFontsize
        }
      }
    },
    methods: {
      show () { // 显示隐藏提示文字
        return this.showTooltips = true;
      },
      hide () {
        return this.showTooltips = false;
      },
      currentPageYOffset () {
        // 判断滚动区域大于多少的时候显示返回顶部的按钮
        window.pageYOffset > this.pageY ? this.showReturnToTop = true : this.showReturnToTop = false;
      },
      backToTop () {
        scrollIt(0, 1500, this.transitionName, this.currentPageYOffset);
      }
    },
    created () {
      window.addEventListener('scroll', this.currentPageYOffset);
    },
    beforeDestroy () {
      window.removeEventListener('scroll', this.currentPageYOffset)
    }
  }
</script>
<style scoped lang="scss">
  .back-to-top {
    position: fixed;
    bottom: 5%;
    right: 100px;
    z-index: 9999;
    cursor: pointer;
    width: auto;
    i {
      font-size: 32px;
      display: inline-block;
      position: relative;
      text-align: center;
      padding: 5px;
      background-color: rgba(234, 231, 231, 0.52);
      border-radius: 5px;
      transition: all 0.3s linear;
      &:hover {
        border-radius: 50%;
        background: #222;
        color: #fff !important;
      }
    }
    .tips {
      display: inline-block;
      position: absolute;
      word-break: normal;
      white-space: nowrap;
      width: auto;
      font-size: 12px;
      color: #fff;
      z-index: -1;
    }
    .left {
      right: 0;
      top: 50%;
      margin-right: 50px;
      transform: translateY(-50%);
    }
    .right {
      left: 0;
      top: 50%;
      margin-left: 50px;
      transform: translateY(-50%);
    }
    .bottom {
      bottom: 0;
      margin-top: 50px;
    }
    .top {
      top: 0;
      margin-bottom: 50px;
    }
  }
</style>
目录
相关文章
|
9天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
9天前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
20天前
|
缓存 JavaScript UED
Vue 中实现组件的懒加载
【10月更文挑战第23天】组件的懒加载是 Vue 应用中提高性能的重要手段之一。通过合理运用动态导入、路由配置等方式,可以实现组件的按需加载,减少资源浪费,提高应用的响应速度和用户体验。在实际应用中,需要根据具体情况选择合适的懒加载方式,并结合性能优化的其他措施,以打造更高效、更优质的 Vue 应用。
|
25天前
|
前端开发 UED
vue3知识点:Suspense组件
vue3知识点:Suspense组件
30 4
|
24天前
|
JavaScript 前端开发 测试技术
组件化开发:创建可重用的Vue组件
【10月更文挑战第21天】组件化开发:创建可重用的Vue组件
24 1
|
25天前
|
JavaScript 前端开发 Java
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
32 2
|
25天前
|
Java
vue3知识点:Teleport组件
vue3知识点:Teleport组件
25 1
|
JavaScript
Vue的非父子组件之间传值
全局事件总线 一种组件间通信的方式,适用于任意组件间通信
|
缓存 JavaScript 前端开发
Vue Props、Slot、v-once、非父子组件间的传值....
Vue Props、Slot、v-once、非父子组件间的传值....
84 0
|
JavaScript
Vue中父子组件传值
先在⽗组件中给⼦组件的⾃定义属性绑定⼀个⽗组件的变量