Vue组件开发系列之自定义滚动Scroll组件

组件源码:
https://github.com/AntJavascript/widgetUI/tree/master/Scroll

自定义组件我主要是用来解决手机端路由返回的时候,页面会自动滚动到顶部的问题

image

组件页面结构:


代码分析:

props参数:

props: {
    scrollbar: { // 是否显示滚动条
      type: Boolean,
      default: () => {
        return false;
      }
    },
    direction: { // 组件滚动方向("V" 代表垂直滚动,"H" 代表水平滚动)
      type: String,
      default: () => {
        return 'v';
      }
    },
    autoHide: { // 滚动条是否会自动隐藏
      type: Boolean,
      default: () => {
        return false;
      }
    }
  }

data参数:

data () {
    return {
      speed: 4, // 速度
      translate: 0, // 滑动距离
      isTop: true, // 是否滑动到了顶部
      isBottom: false, // 是否滑动到了底部
      distance: 0, // 滑动距离
      hisdistance: 0, // 上一次滑动距离
      maxDistance: '', // 最大滑动距离
      start: { // 触摸坐标
        X: 0,
        Y: 0
      },
      move: { // 移动坐标
        X: 0,
        Y: 0
      },
      scrollHeight: '', // 滚动条背景高度
      scrollWidth: '', // 滚动条背景宽度
      scrollRatio: '', // 滚动条比例
      startTime: '', // 触摸开始时间
      endTime: '', // 触摸结束时间
      transtionTime: '0', // 过渡时间
      timer: '', // 定时器
      status: '' // 状态
    };
  }

touch事件:

touchStart // 手指接触屏幕触发

touchStart () {
      // 触摸坐标
      this.start.X = event.touches[0].clientX;
      this.start.Y = event.touches[0].clientY;
      this.startTime = new Date().getTime();
    }

touchmove // 滑动手指的时候触发

touchMove () {
      var self = this;
      event.preventDefault();
      self.setTranstionTime('0ms');
      // 清除定时器,不清除的话,连续滑动会导致滚动条渐隐渐现
      clearTimeout(self.timer);
      // 滑动时候的坐标
      this.move.X = event.touches[0].clientX;
      this.move.Y = event.touches[0].clientY;
      // 滑动距离
      var tance = '';
      if (this.direction.toLocaleUpperCase() === 'H') {
        tance = -(this.move.X - this.start.X); // 本次滑动距离
      } else {
        tance = this.move.Y - this.start.Y; // 本次滑动距离
      }
      // 如果当前处于顶部或者底部,就增加阻力
      if (this.hisdistance === 0 || Math.abs(this.hisdistance) === this.maxDistance) {
        tance = tance * 0.5;
      }
      this.distance = this.hisdistance + tance; // 页面滑动距离(上一次滑动距离 + 本次滑动距离)
      this.status = 'moveing'
    }

touchend // 手指离开触发

touchEnd () {
      var self = this;
      self.endTime = new Date().getTime();
      // this.move.Y 说明没有滑动
      if (this.direction.toLocaleUpperCase() === 'V') {
        if (this.move.Y === 0) {
          return;
        }
      }
      // 如果触摸时间超过500ms,则不加速滑动
      if (self.endTime - self.startTime > 500) {
        self.speed = 1;
      } else {
        self.speed = 4;
      }
      var thisTanceX = this.move.X - this.start.X; // 本次滑动是X距离
      var thisTanceY = this.move.Y - this.start.Y; // 本次滑动是Y距离
      // 设置过渡时间
      self.setTranstionTime('1000ms');
      if (this.direction.toLocaleUpperCase() === 'H') {
        // 水平滚动
        // 如果是触顶或者触底了,就把设置时间设置为300ms
        if (this.hisdistance + -thisTanceX * self.speed <= 0 || Math.abs(this.hisdistance + -thisTanceX * self.speed) >= this.maxDistance) {
          self.setTranstionTime('300ms');
        }
        this.distance = this.hisdistance + -thisTanceX * self.speed;
      } else {
        // 垂直滚动
        // 如果是触顶或者触底了,就把设置时间设置为300ms
        if (this.hisdistance + thisTanceY * self.speed >= 0 || Math.abs(this.hisdistance + thisTanceY * self.speed) >= this.maxDistance) {
          self.setTranstionTime('300ms');
        }
        this.distance = this.hisdistance + thisTanceY * self.speed;
      }
      if (this.direction.toLocaleUpperCase() === 'H') {
        // 水平滚动
        if (self.distance <= 0) {
          self.distance = 0;
          self.hisdistance = 0; // 清除上一次滑动距离
          self.isTop = true; // 滑动到了顶部
        } else if (Math.abs(self.distance) >= this.maxDistance) {
          self.isBottom = true;
          self.distance = this.maxDistance;
          self.hisdistance = self.distance; // 记录上次滑动的位置
        } else {
          self.isTop = false;
          self.isBottom = false;
          self.hisdistance = self.distance; // 记录上次滑动的位置
        }
      } else {
        // 垂直滚动
        // self.distance >= 0 说明滑动到了顶部
        if (self.distance >= 0) {
          self.distance = 0; // 滑动距离等于0
          self.hisdistance = 0; // 清除上一次滑动距离
          self.isTop = true; // 滑动到了顶部
        } else if (Math.abs(self.distance) >= this.maxDistance) {
          self.isBottom = true; // 滑动到了底部
          self.distance = -this.maxDistance; // 滑动距离等于最大滑动距离
          self.hisdistance = self.distance; // 记录上次滑动的位置
        } else {
          self.isTop = false;
          self.isBottom = false;
          self.hisdistance = self.distance; // 记录上次滑动的位置
        }
      }
      if (this.direction.toLocaleUpperCase() === 'H') {
        // 不能设置为0,否则触摸手机边界会有问题
        // self.move.X = 0;
      } else {
        self.move.Y = 0;
      }
      self.timer = setTimeout ( () => {
        this.status = '' // 清空状态
      }, ~~self.transtionTime + 300)
    }

mounted 生命周期,初始化工作

mounted () {
    var el = this.$el;
    var wrapper = el.firstChild;
    // 如果是横向滚动
    if (this.direction.toLocaleUpperCase() === 'H') {
      this.$nextTick(() => {
        var itemConutWidth = 0; // wrapper总宽度
        var len = wrapper.childElementCount; // wrapper 的子节点数量
        // 这个for循环用于计算  wrapper 所有子节点的宽度
        for (let i = 0; i < len; i++) {
          // 获取元素的marginLeft值
          var marginL= parseFloat(getComputedStyle(wrapper.children[i], false)['marginLeft'].replace('px', ''));
          // 获取元素的marginRight值
          var marginR= parseFloat(getComputedStyle(wrapper.children[i], false)['marginRight'].replace('px', ''));
          itemConutWidth+= wrapper.children[i].offsetWidth + marginL + marginR;
        }
        wrapper.style.width = itemConutWidth - el.offsetWidth + 'px'; // 设置 wrapper 的宽度
        this.maxDistance = itemConutWidth - el.offsetWidth; // 设置可滚动的最大距离
        this.scrollWidth = (el.offsetWidth / this.maxDistance) * el.offsetWidth; // 设置滚动条宽度(水平滚动时可用到)
        this.scrollRatio = (el.offsetWidth / itemConutWidth); // 设置滚动条比例
      });
      wrapper.style.display = 'flex'; // 设置display属性为 "flex"
    } else {
      /* 垂直滚动设置 */
      // 如果高度大于整个屏幕高度,则滚动区域高度等于屏幕高度
      var elHeight = el.clientHeight < MediaQuery.height ? el.clientHeight : MediaQuery.height;
      this.scrollHeight = elHeight * 0.98; // 设置滚动条高度
      this.maxDistance = wrapper.offsetHeight - elHeight; // 设置可滚动的最大距离
      this.scrollRatio = this.scrollHeight / wrapper.offsetHeight; // 设置滚动条比例
    }
  }

组件源码:
https://github.com/AntJavascript/widgetUI/tree/master/Scroll

你可能感兴趣的:(Vue组件开发系列之自定义滚动Scroll组件)