热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

详解vuemint-ui源码解析之loadmore组件

本文介绍了vue mint-ui源码解析之loadmore组件,分享给大家,具体如下: 接入 官方接入文档mint-ui loadm

本文介绍了vue mint-ui源码解析之loadmore组件,分享给大家,具体如下:

接入

官方接入文档mint-ui loadmore文档

接入使用Example

html

Loading... 释放更新
  • {{ item }}

css


*{
  margin: 0;
  padding: 0;
}
html, body{
  height: 100%;
}

#app{

  height: 100%;
  overflow: scroll;
}
.scroll-wrapper{
  margin: 0;
  padding: 0;
  list-style: none;

}
.scroll-wrapper li{
  line-height: 120px;
  font-size: 60px;
  text-align: center;
}

js 







实现原理解析

布局原理

  • loadmore组件内部由三个slot组成,分别为name=top,name=bottom,default;
  • top用于展示下拉刷新不同状态展示的内容,初始设置margin-top为-top的高度来将自己隐藏
  • bottom同top,用于展示上拉加载更多不同状态展示的内容
  • default填充滚动详细内容

实现原理

  • 主要是通过js的touch事件的监听来实现
  • 在touchmove事件,如果是向下滑动并且滚动的dom的scrollTop为0,那么整个组件向下偏移(滑动的距离/比值)展示出top solt的内容
  • 在touchmove时间,如果是向上滑动并且滑动到了底部,再继续滑动整个组件向上偏移(滑动的距离/比值)展示出bottom solt的内容

源码解析

组件的template html

 
{{ topText }}
{{ bottomText }}

关于 上面的spinner标签,是一个组件,这里不做详细介绍。top solt和bottom slot中的内容是展示的内容,可以通过外部自定义的方式传入。

其实它的实现有一个很严重的弊端,就是写死了top solt和bottom slot的高度为50px,而且js中的处理也是使用50px进行的逻辑处理。所以并满足我们开发中自定义top slot 和bottom slot的需求。

js核心解析

  • props解析:关于props的解析,可以参考mint-ui的官方文档
  • data解析
data() {
 return {
  translate: 0, // 此变量决定当前组件上下移动,
  scrollEventTarget: null, // 滚动的dom节点
  containerFilled: false, // 当前滚动的内容是否填充完整,不完成会调用 loadmore的回调函数
  topText: '', // 下拉刷新,显示的文本
  topDropped: false, // 记录当前drop状态,用给组件dom添加is-dropped class(添加回到原点的动画)
  bottomText: '', // 上拉加载更多 显示的文本
  bottomDropped: false, // 同topDropped
  bottomReached: false, // 当前滚动是否滚动到了底部
  direction: '', // touch-move过程中, 当前滑动的方向
  startY: 0, // touch-start 起始的y的坐标值
  startScrollTop: 0, // touch-start 起始的滚动dom的 scrollTop
  currentY: 0, // touch-move 过程中的 y的坐标
  topStatus: '', // 下拉刷新的状态: pull(下拉) drop(释放) loading(正在加载数据)
  bottomStatus: '' // 上拉加载更多的状态: 状态同上
 };
}

上面的关于每个data数据的具体作用通过注释做了详细说明。

watch解析

watch: {
 topStatus(val) {
  this.$emit('top-status-change', val);
  switch (val) {
   case 'pull':
    this.topText = this.topPullText;
    break;
   case 'drop':
    this.topText = this.topDropText;
    break;
   case 'loading':
    this.topText = this.topLoadingText;
    break;
  }
 },

 bottomStatus(val) {
  this.$emit('bottom-status-change', val);
  switch (val) {
   case 'pull':
    this.bottomText = this.bottomPullText;
    break;
   case 'drop':
    this.bottomText = this.bottomDropText;
    break;
   case 'loading':
    this.bottomText = this.bottomLoadingText;
    break;
  }
 }
}

上面是组件通过watch监听的两个变量,后面我们能看到他们的改变是在touchmove事件中进行处理改变的。它的作用是通过它的变化来改变top slot和bottom slot的文本内容;

同时发出status-change事件给外部使用,因为可能外部自定义top slot 和bottom slot的内容,通过此事件来通知外部当前状态以便外部进行处理。

核心函数的解析

这里就不将所有的method列出,下面就根据处理的所以来解析对应的method函数。

首先,入口是在组件mounted生命周期的钩子回调中执行init函数

mounted() {
 this.init();// 当前 vue component挂载完成之后, 执行init()函数
}

init函数:

init() {
  this.topStatus = 'pull';
  this.bottomStatus = 'pull';
  this.topText = this.topPullText;
  this.scrollEventTarget = this.getScrollEventTarget(this.$el); // 获取滚动的dom节点
  if (typeof this.bottomMethod === 'function') {
   this.fillContainer(); // 判断当前滚动内容是否填满,没有执行外部传入的loadmore回调函数加载数据
   this.bindTouchEvents(); // 为当前组件dom注册touch事件
  }
  if (typeof this.topMethod === 'function') {
   this.bindTouchEvents();
  }
 },

 fillContainer() {
  if (this.autoFill) {
   this.$nextTick(() => {
    if (this.scrollEventTarget === window) {
     this.cOntainerFilled= this.$el.getBoundingClientRect().bottom >=
      document.documentElement.getBoundingClientRect().bottom;
    } else {
     this.cOntainerFilled= this.$el.getBoundingClientRect().bottom >=
      this.scrollEventTarget.getBoundingClientRect().bottom;
    }
    if (!this.containerFilled) { // 如果没有填满内容, 执行loadmore的操作
     this.bottomStatus = 'loading';
     this.bottomMethod();// 调用外部的loadmore函数,加载更多数据
    }
   });
  }
 }

init函数主要是初始化状态和事件的一些操作,下面着重分析touch事件的回调函数的处理。

首先touchstart事件回调处理函数

 handleTouchStart(event) {
  this.startY = event.touches[0].clientY; // 手指按下的位置, 用于下面move事件计算手指移动的距离
  this.startScrollTop = this.getScrollTop(this.scrollEventTarget); // 起始scroll dom的 scrollTop(滚动的距离)
  //下面重置状态变量
  this.bottomReached = false;
  if (this.topStatus !== 'loading') {
   this.topStatus = 'pull';
   this.topDropped = false;
  }
  if (this.bottomStatus !== 'loading') {
   this.bottomStatus = 'pull';
   this.bottomDropped = false;
  }
 }

主要是记录初始位置和重置状态变量。

下面继续touchmove的回调处理函数

 handleTouchMove(event) {
  //确保当前touch节点的y的位置,在当前loadmore组件的内部
  if (this.startY  this.$el.getBoundingClientRect().bottom) {
   return;
  }
  this.currentY = event.touches[0].clientY;
  let distance = (this.currentY - this.startY) / this.distanceIndex;
  this.direction = distance > 0 ? 'down' : 'up';
  // 下拉刷新,条件(1.外部传入了刷新的回调函数 2.滑动方向是向下的 3.当前滚动节点的scrollTop为0 4.当前topStatus不是loading)
  if (typeof this.topMethod === 'function' && this.direction === 'down' &&
   this.getScrollTop(this.scrollEventTarget) === 0 && this.topStatus !== 'loading') {
   event.preventDefault();
   event.stopPropagation();
   //计算translate(将要平移的距离), 如果当前移动的距离大于设置的最大距离,那么此次这次移动就不起作用了
   if (this.maxDistance > 0) {
    this.translate = distance <= this.maxDistance &#63; distance - this.startScrollTop : this.translate;
   } else {
    this.translate = distance - this.startScrollTop;
   }
   if (this.translate <0) {
    this.translate = 0;
   }
   this.topStatus = this.translate >= this.topDistance &#63; 'drop' : 'pull';// drop: 到达指定的阈值,可以执行刷新操作了
  }

  // 上拉操作, 判断当前scroll dom是否滚动到了底部
  if (this.direction === 'up') {
   this.bottomReached = this.bottomReached || this.checkBottomReached();
  }
  if (typeof this.bottomMethod === 'function' && this.direction === 'up' &&
   this.bottomReached && this.bottomStatus !== 'loading' && !this.bottomAllLoaded) {
   event.preventDefault();
   event.stopPropagation();
   // 判断的逻辑思路同上
   if (this.maxDistance > 0) {
    this.translate = Math.abs(distance) <= this.maxDistance
     &#63; this.getScrollTop(this.scrollEventTarget) - this.startScrollTop + distance : this.translate;
   } else {
    this.translate = this.getScrollTop(this.scrollEventTarget) - this.startScrollTop + distance;
   }
   if (this.translate > 0) {
    this.translate = 0;
   }
   this.bottomStatus = -this.translate >= this.bottomDistance &#63; 'drop' : 'pull';
  }
  this.$emit('translate-change', this.translate);
 }

上面的代码逻辑挺简单,注释也就相对不多。

重点谈一下checkBottomReached()函数,用来判断当前scroll dom是否滚动到了底部。

 checkBottomReached() {
  if (this.scrollEventTarget === window) {
   return document.body.scrollTop + document.documentElement.clientHeight >= document.body.scrollHeight;
  } else {
   return this.$el.getBoundingClientRect().bottom <= this.scrollEventTarget.getBoundingClientRect().bottom + 1;
  }
 }

经过我的测试,上面的代码是有问题:

当scrollEventTarget是window的条件下,上面的判断是不对的。因为document.body.scrollTop总是比正常值小1,所以用于无法满足到达底部的条件;

当scrollEventTarget不是window的条件下,上面的判断条件也不需要在this.scrollEventTarget.getBoundingClientRect().bottom后面加1,但是加1也不会有太大视觉上的影响。

最后来看下moveend事件回调的处理函数

 handleTouchEnd() {
  if (this.direction === 'down' && this.getScrollTop(this.scrollEventTarget) === 0 && this.translate > 0) {
   this.topDropped = true; // 为当前组件添加 is-dropped class(也就是添加动画处理)
   if (this.topStatus === 'drop') { // 到达了loading的状态
    this.translate = '50'; // top slot的高度
    this.topStatus = 'loading';
    this.topMethod(); // 执行回调函数
   } else { // 没有到达,回调原点
    this.translate = '0';
    this.topStatus = 'pull';
   }
  }
  // 处理逻辑同上
  if (this.direction === 'up' && this.bottomReached && this.translate <0) {
   this.bottomDropped = true;
   this.bottomReached = false;
   if (this.bottomStatus === 'drop') {
    this.translate = '-50';
    this.bottomStatus = 'loading';
    this.bottomMethod();
   } else {
    this.translate = '0';
    this.bottomStatus = 'pull';
   }
  }
  this.$emit('translate-change', this.translate);
  this.direction = '';
 }
}

总结

  1. 下拉刷新和上拉加载更多的实现原理可以借鉴
  2. getScrollEventTarget()获取滚动对象,getScrollTop()获取滚动距离,checkBottomReached()判断是否滚动到底部;这三种方式可以借鉴
  3. 缺点: 写死了top slot 和 bottom slot的高度,太不灵活;这个地方可以优化

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 地球坐标、火星坐标及百度坐标间的转换算法 C# 实现
    本文介绍了WGS84坐标系统及其精度改进历程,探讨了火星坐标系统的安全性和应用背景,并详细解析了火星坐标与百度坐标之间的转换算法,提供了C#语言的实现代码。 ... [详细]
  • ServletContext接口在Java Web开发中扮演着重要角色,它提供了一种方式来获取关于整个Web应用程序的信息。通过ServletContext,开发者可以访问初始化参数、共享数据以及应用资源。 ... [详细]
  • 在Kubernetes集群中部署Kuboard
    本文详细介绍了如何在Kubernetes(简称k8s)环境中部署Kuboard,包括必要的命令和步骤,帮助用户顺利完成安装。 ... [详细]
  • 本文介绍并分享了三个个人开源项目,涵盖单元测试中HttpContext的可测试性增强、Visual Studio插件开发以及单元测试报告自动生成工具。 ... [详细]
  • 本文详细记录了利用HTML5和CSS3进行企业网站开发的过程,包括技术要点、实践技巧以及常见问题解决方法。 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 如何撰写初级和高级前端开发者的专业简历
    如何撰写初级和高级前端开发者的专业简历 ... [详细]
  • 本文详细介绍了在 Vue.js 前端框架中集成 vue-i18n 插件以实现多语言支持的方法。通过具体的配置步骤和示例代码,帮助开发者快速掌握如何在项目中实现国际化功能,提升用户体验。同时,文章还探讨了常见的多语言切换问题及解决方案,为开发人员提供了实用的参考。 ... [详细]
  • MyEclipse技巧:高效生成toString方法
    本文将介绍如何在MyEclipse中快速且高效地生成toString方法,帮助开发者简化编码过程,提高开发效率。 ... [详细]
  • 本文介绍了如何使用命令行在 Windows 系统中启动或关闭 VMWare 的关键服务,包括 VMwareHostd、VMAuthdService、VMUSBArbService、VMware NAT Service 和 VMnetDHCP。 ... [详细]
  • 主板市盈率、市净率及股息率的自动化抓取
    本文介绍了如何通过Python脚本自动从中国指数有限公司网站抓取主板的市盈率、市净率和股息率等关键财务指标,并将这些数据存储到CSV文件中。涉及的技术包括网页解析、正则表达式以及异常处理。 ... [详细]
  • 本文详细介绍了在Delphi环境中对DLL文件进行断点调试的方法,包括设置依赖的可执行文件、编译器和链接器的调试选项,以及运行时参数的配置。 ... [详细]
  • 本文介绍如何创建一个专门用于处理浮点数的JSON处理器,并将其注册到JSON配置器中,以实现对浮点数的精确控制和格式化输出。 ... [详细]
  • 详细的介绍针对graphiclayer的空间查询。首先,空间查询的方式:提供多种类型的空间查询,包括点周边、线周边、面内等多种方式;其次,图形绘制完成后状态的展示;再次 ... [详细]
  • 本文精选了几所优秀的PHP实训和培训学校,为希望深入学习PHP编程的学员提供参考。 ... [详细]
author-avatar
陈爱梅志杰力凯
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有