实现vue返回顶部backTop组件

2,982 阅读2分钟

概述

在一些页面比较长的时候,可能会用到回到顶部功能,一般都是右下角一个图标我们点击可以返回到顶部,这个功能还是蛮常见的在一些网页中,这里实现一个跟element-ui组件库功能一样的backTop组件。这个功能看起来还是蛮简单的,我们可以自由发挥。

最终效果

  • 不带滚动父元素

动画.gif

  • 带滚动父元素

动画.gif

实现原理

实现这个功能其实就是给滚动元素添加滚动事件,当超出某个滚动距离的时候显示,反之隐藏,中间我们可以额外加一些过渡动画和节流函数来提升用户体验。

具体实现

App.vue

<template>
  <div id="app" class="container" :style="{ height: 2000 + 'px' }">
    <div class="test-date-picker">
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <p>this is test scroll event</p>
      <!-- 不带滚动父元素的 -->
      <back-top
        :visibility-height="200"
        @backUpClick="handleBackUpClick"
      >
      </back-top>

      <!-- 带滚动父元素的 -->
      <!-- <back-top
        target=".test-date-picker"
        :visibility-height="200"
        @backUpClick="handleBackUpClick"
      >
      </back-top> -->
    </div>
  </div>
</template>

<script>
import BackTop from "./components/BackTop/index.js";
export default {
  name: "App",
  components: {
    BackTop,
  },
  methods: {
   //点击回到顶部按钮触发事件
    handleBackUpClick(e) {
      console.log(e);
    },
  },
};
</script>

<style lang="less">
#app {
  .test-date-picker {
    width: 400px;
    margin: 20px auto;
    height: 300px;
    background-color: #e8f3fe;
    overflow: auto;
  }
}
</style>

./components/BackUp/BackTop.vue

<template>
  <transition name="back-up-fade">
    <div
      class="back-top"
      :style="{
        bottom: bottom + 'px',
        right: right + 'px',
      }"
      @click.stop="handleBackTopClick($event)"
      v-if="show"
    >
      <div class="back-text-contaner">
        <!-- 提供了插槽显示插槽内容 -->
        <slot v-if="$slots.default"></slot>
        <!-- 没提供,使用默认的 -->
        <div class="default-text" v-else>UP</div>
      </div>
    </div>
  </transition>
</template>

<script>
// 导入节流函数
import { throttle } from "./utils";
export default {
  props: {
    // 触发滚动的容器
    target: {
      type: String,
      default: "",
    },
    // 滚动高度达到此参数值才出现
    visibilityHeight: {
      type: Number,
      default: 200,
    },
    // 控制其显示位置, 距离页面右边距
    right: {
      type: Number,
      default: 40,
    },
    // 控制其显示位置, 距离页面底部距离
    bottom: {
      type: Number,
      default: 40,
    },
  },
  data() {
    return {
      // 滚动容器
      container: null,
      // 是否显示
      show: false,
      // 定时器标识
      timer: null,
      // 包裹元素节点
      el: "",
    };
  },
  mounted() {
    //节流处理,避免频繁触发事件
    this.throttleHandleContanierScroll = throttle(
      this.handleContanierScroll,
      200,
      false
    );
    this.init();
  },
  methods: {
    init() {
      // 默认滚动元素为document
      this.container = document;
      this.el = document.documentElement;
      // 外部传入滚动包裹元素时候
      if (this.target) {
        const el = document.querySelector(this.target);
        el ? (this.container = el && (this.el = el)) : null;
      }
      console.log(this.container);
      this.container.addEventListener(
        "scroll",
        this.throttleHandleContanierScroll,
        false
      );
    },
    // 回到顶部点击
    handleBackTopClick(e) {
      // 给回到顶部一定过度事件
      this.timer = setInterval(() => {
        this.scrollToTop();
      }, 16);
      // 通知用户
      this.$emit("backUpClick", e);
    },
    // 滚动事件
    handleContanierScroll() {
      // 滚动超出给定高度则显示,否则隐藏
      if (this.el.scrollTop > this.visibilityHeight) {
        this.show = true;
      } else {
        this.show = false;
      }
    },
    // 滚动到顶部
    scrollToTop() {
      if (this.el.scrollTop > 0) {
        this.el.scrollTop -= 50;
      } else {
        clearInterval(this.timer);
      }
    },
  },
  beforeDestroy() {
    // 销毁定时器
    clearInterval(this.timer);
    this.timer = null;
  },
};
</script>

<style lang="less">
.back-top {
  position: fixed;
  background-color: #fff;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: #409eff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  box-shadow: 0 0 6px rgb(0 0 0 / 12%);
  cursor: pointer;
  z-index: 5;
}
.back-up-position {
  position: relative;
}

// 过度动画
.back-up-fade-enter-active {
  transition: opacity 0.3s linear;
}
.back-up-fade-enter {
  opacity: 0;
}
.back-up-leave-to {
  opacity: 0;
}
</style>

./components/BackUp/utils.js

/**
 * @Description 节流函数
 * @param { Function } fn 需要做节流的函数
 * @param { Function } intervalTime 间隔实践
 * @param { Function } immediate 是否立即执行
 * @return { Number } intervalTime
 **/
export function throttle(fn, intervalTime = 200, immediate = true) {
  let oldTime = Date.now();
  return function (...arg) {
    if (immediate) {
      fn.bind(this)(...arg);
      immediate = false;
    }
    let nowTime = Date.now();
    if (nowTime - oldTime >= intervalTime) {
      fn.bind(this)(...arg);
      oldTime = nowTime;
    }
  };
}

./components/BackUp/index.js

//按需导出组件
import BackTop from "./BackTop.vue";

export default BackTop;

总结

这个组件还是比较简单的,我们可以根据自己需要自行扩展,上面提到的节流函数不懂的可以查下,防抖和节流函数在性能优化上面还是挺常用的。