阅读 2732

Vue 滚动触底 mixins

前言

在app端常常看到类似加载数据的动画,接下来我们来实现滚动触底加载动画提示,以及如何复用这些逻辑。

如何判断滚动触底

来看下几张图:

情况一:

当文档高度还为超过可视区域高度时,不存在滚动,所以也没有滚动触底

情况二:

当文档高度超过可视区域的高度时,还有剩余的文档没有滚动完,也就是说可视区域高度 + 滚动高度 < 文档高度,此时没有达到滚动触底的条件

情况三:

文档高度大于可视区域,并且滚动到文档底部, 也就是说 可视区域高度 + 滚动高度 = 文档高度

判断是否滚动到底

经过上面三种情况的分析,我们需要拿到 可视区域的高度 , 滚动高度 , 文档高度这三个变量来进行比较。

可视区域的高度

function getWindowHeight() {
  return document.documentElement.clientHeight;
}
复制代码

滚动高度

对有doctype申明的页面使用document.documentElement.scrollTop,safari特例独行:使用 window.pageYOffset

function getScrollHeight() {
  return Math.max(document.documentElement.scrollTop,window.pageYOffset||0)
}
复制代码

文档高度

function getDocumentTop() {
  return document.documentElement.offsetHeight;
}
复制代码

代码实现

触底打印

codepen 触底打印demo

  • 通过监听滚动事件来判断 可视区域滚动高度文档高度的关系,实现最基础的触底加载
  <div id="app">
   <ul>
      <li v-for="item in  list" :key="item" > {{item}}</li>
    </ul>
 </div>
 created(){
      // 初始化数据
      this.list =  Array.from(Array(10),(item,index)=>index)
      // 通过监听滚动事件来判断 可视区域 , 滚动高度 ,文档高度的关系
      window.addEventListener('scroll',()=>{
        let isBottom = (getScrollHeight() +  getWindowHeight()) >=  getDocumentTop()
        if(isBottom){
          console.log('触底了',new Date())
          let list = this.list
          let last = list[list.length-1]
          let newList = Array.from(Array(10),(item,index)=>index+last+1)
          this.list.push(...newList)
        }
      })
  } 
复制代码

优化和复用滚动事件逻辑

将滚动逻辑抽取成 mixins 放在 scroll.js 中。优化功能点如下:

  1. 增加触底距离
  2. 滚动事件监听事件节流
  3. 添加触底动画,并将 UI 样式一起封装在 scroll.js 中

模拟请求事件

为了模拟请求数据,封装了一个 Promise 一秒后返回结果

methods:{
  // 返回一个 promise ,用于请求服务端数据
  findDataList(){
    let list = this.list
    let last = list[list.length-1]
    return new Promise((resolve)=>{
    // 模拟服务端数据
    let newList = Array.from(Array(10),(item,index)=>index+last+1)
      setTimeout(() => {
        resolve(newList)
      }, 1000);
    })
  }
}
复制代码

滚动事件监听

滚动事件触发,判断当前是否触底,触底了以后去执行 loadMore 发起请求拿取服务端的数据

  created(){
    let fn = throttle(()=>{
      let isOver = (getScrollHeight() +  getWindowHeight()) >=  (getDocumentTop()- MIN_INSTANCE)
      // 触底时进行数据加载
      if(isOver){
        // 创建加载组件
        this.loadMore&&this.loadMore()
      }
    },DEALY_TIME)
    window.addEventListener('scroll',fn)
  },
复制代码

添加触底动画

因为我们是将逻辑抽离在 mixins中,为了把触底动画也集成在里面使用 Vue.extend() 来实现编程式插入UI样式的方法。

  1. 首先定义好 loading 组件的样式
<template>
    <div id="loading-alert">
      <i class="el-icon-loading"></i>
      <span>{{ message }}</span>
    </div>
</template>

<script>
export default {
  props:{
    message:{
      type:String,
      default:'正在加载更多数据'
    }
  },
};
复制代码
  1. 当触底时去插入这个 loading 组件
import load from './load.vue'
data(){
  return {
    isLoading:false,
    component:null
  }
},
created(){
  let fn = throttle(()=>{
    let isOver = (getScrollHeight() +  getWindowHeight()) >=  (getDocumentTop()- MIN_INSTANCE)
    // 触底时进行load组件显示
    if(isOver){
      // 判断loading组件是否已经存在,不存在就创建一个
      if(!this.component){
        this.component = extendComponents(load)
      }
      // 创建加载组件
      this.loadMore&&this.loadMore()
      // 判断当前状态来控制loading的组件显示与否
      if(!this.isLoading){
        this.component.$el.remove()
        // 将loading组件置为空
        this.component = null
      }
    }
  },DEALY_TIME)
  window.addEventListener('scroll',fn)
},
复制代码

详细代码

完整代码可以点击查看 codepen 触底动画 关于上面代码中 extendComponents 方法的实现可以查看详细代码,也可以查看 Vue.extend 编程式插入组件

最后

将滚动触底的逻辑和 UI 都集成到 scroll.js 中,复用都方式可以放在 mixins 可以抽离成 v-directive,这样我们可以接受到绑定的 dom 不仅仅可以做 window 的滚动触底事件的判断,也可以实现单个元素的滚动事件触底的监听。后续可以在实现 v-directive 的版本。

关注下面的标签,发现更多相似文章
评论