element ui backTop源码解析-逐行逐析

backTop 回到顶部 组件简介

基础概念

返回页面顶部的操作按钮

代码

<template>
  <div>
    <transition name="el-fade-in">
      <div
        v-if="visible"
        @click.stop="handleClick"
        :style="{
          'right': styleRight,
          'bottom': styleBottom
        }"
        class="el-backtop">
        <slot>
          <el-icon name="caret-top">el-icon>
        slot>
      div>
      
    transition>


  div>
template>

<script>
import throttle from 'throttle-debounce/throttle';

const cubic = value => Math.pow(value, 3);
// Math.pow(a,b) 用于计算指定值的次方值,此处是a的b次方
// 此处用到的是箭头函数
const easeInOutCubic = value => value < 0.5
  ? cubic(value * 2) / 2
  : 1 - cubic((1 - value) * 2) / 2;

export default {
  name: 'ElBacktop',
  props: {
    visibilityHeight: {//滚动达到此参数值才出现
      type: Number,
      default: 200
    },
    target: [String],//触发滚动的对象 滚动的容器
    right: {//控制显示位置,距离页面右边距
      type: Number,
      default: 40
    },
    bottom: {//控制显示位置,距离页面顶部距离 
      type: Number,
      default: 40
    }
  },

  data() {
    return {
      el: null,// 触发滚动的对象
      container: null,//滚动的容器
      visible: false//是否可见  页面滚动距离大于指定高度
    };
  },

  computed: {
    styleBottom() {
      return `${this.bottom}px`;
    },
    styleRight() {
      return `${this.right}px`;
    }
  },

  mounted() {
    this.init();
    this.throttledScrollHandler = throttle(300, this.onScroll);
    // 节流 封装一个 函数 将onScroll 包裹一层,限制函数触发的频率
    this.container.addEventListener('scroll', this.throttledScrollHandler);
    // 滚动容器上绑定滚动事件,onScroll 
  },

  methods: {
    init() {
      this.container = document;
      this.el = document.documentElement;
      //document 和 documentElement 的区别
      // document 是文档对象 
      // document.documentElement 是整个文档节点树的根节点元素 即元素
      // container 容器 设置为 document 
      //当前元素的                                                                                                      
      if (this.target) {
        // 有传入选择器 
        this.el = document.querySelector(this.target);
        // 通过选择器获取元素
        if (!this.el) {
          // 如果没有获取到元素则抛出错误
          throw new Error(`target is not existed: ${this.target}`);
        }
        this.container = this.el;
        // 滚动的容器设置为el 
      }
    },
    onScroll() {
      const scrollTop = this.el.scrollTop;//获取滚动条距离顶部的距离
      this.visible = scrollTop >= this.visibilityHeight;//页面滚动距离大于指定高度
    },
    handleClick(e) {
      /* 
         在触发点击的时候
         调用scrollToTop 页面滚动到顶部
         并且向父组件发送click事件,给组件模拟原生的click事件,以方便父组件调用的时候有点击事件的逻辑

      */
      this.scrollToTop();
      // 调用滚动到顶部的函数
      this.$emit('click', e);//向父组件发送click事件,给组件模拟原生的click事件,以方便父组件调用的时候有点击事件的逻辑
    },
    scrollToTop() {
      const el = this.el;
      const beginTime = Date.now();//Date.now()获取到当前时间的时间戳
      const beginValue = el.scrollTop;//scrollTop 容器向上滑动后超出rong'qi
      const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16));
      // 会把每一帧中的所有dom操作集中起来,在一次重绘或重流中完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
      // 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量 
      // requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
      const frameFunc = () => {
        const progress = (Date.now() - beginTime) / 500;
        // 计算时间间隔 是否大于 500毫秒 
        if (progress < 1) {
          el.scrollTop = beginValue * (1 - easeInOutCubic(progress));
          // scrollTop 值会依次减小
          rAF(frameFunc);
        } else {
          el.scrollTop = 0;
        }
        // 在指定间隔时间里 递归调用 scrollTop 会逐渐减少,然后直到 progress值为1的时候就直接到顶
      };
      rAF(frameFunc);
    }
  },
  events: {
    // 
    scroll() {
      this.onScroll();
    }
  },
  mounted(){
    console.log("this0000",this)
  },
  beforeDestroy() {
    this.container.removeEventListener('scroll', this.throttledScrollHandler);
    // 在组件销毁的生命周期里 移除 container 上绑定的scroll事件
  }
};
script>

具体分析

props 参数说明

参数名 参数说明
visibilityHeight 页面滚动出去的距离大于此值时才显示组件 visible 为true
target 触发滚动的对象 滚动的容器
right 控制显示位置,距离页面右边距
bot tom 控制显示位置,距离页面顶部距离

data 参数说明

参数名 说明
el 触发滚动的对象
container 滚动的容器
visible 是否可见 页面滚动距离大于指定高

computed

  • styleBottom 根据传入的bottom拼接成px为单位的字符串
  • styleRight 根据传入的 right 拼接成px为单位的字符串

mounted

  • 调用初始化事件
  • 声明一个节流函数
  • 给container元素注册滚动的监听事件

methods

init 初始化函数
  • 根据条件判断 给 el 和 container 赋值
onScroll 滚动的函数
  • 动态获取到el的scrollTop值
  • 格努scrollTop值和visibilityHeight对比,给visible 赋值
handleClick 点击事件
  • 在触发点击的时候 调用scrollToTop 页面滚动到顶部
  • 并且向父组件发送click事件,给组件模拟原生的click事件,以方便父组件调用的时候有点击事件的逻辑
scrollToTop 滚动到顶部
  scrollToTop() {
      const el = this.el;
      const beginTime = Date.now();//Date.now()获取到当前时间的时间戳
      const beginValue = el.scrollTop;//scrollTop 容器向上滑动后超出rong'qi
      const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16));
      // 会把每一帧中的所有dom操作集中起来,在一次重绘或重流中完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
      // 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量 
      // requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
      const frameFunc = () => {
        const progress = (Date.now() - beginTime) / 500;
        // 计算时间间隔 是否大于 500毫秒 
        if (progress < 1) {
          el.scrollTop = beginValue * (1 - easeInOutCubic(progress));
          // scrollTop 值会依次减小
          rAF(frameFunc);
        } else {
          el.scrollTop = 0;
        }
        // 在指定间隔时间里 递归调用 scrollTop 会逐渐减少,然后直到 progress值为1的时候就直接到顶
      };
      rAF(frameFunc);
    }
注意 window.requestAnimationFrame
  • 会把每一帧中的所有dom操作集中起来,在一次重绘或重流中完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
  • 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量
  • requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销

你可能感兴趣的:(element,ui,源码逐行解析,ui,vue.js,前端,element,ui,backtop)