来看下几张图:vue
当文档高度还为超过可视区域高度时,不存在滚动,因此也没有滚动触底 编程
当文档高度超过可视区域的高度时,还有剩余的文档没有滚动完,也就是说可视区域高度 + 滚动高度 < 文档高度
,此时没有达到滚动触底的条件 promise
文档高度大于可视区域,而且滚动到文档底部, 也就是说 可视区域高度 + 滚动高度 = 文档高度
bash
通过上面三种状况的分析,咱们须要拿到 可视区域的高度
, 滚动高度
, 文档高度
这三个变量来进行比较。app
function getWindowHeight() {
return document.documentElement.clientHeight;
}
复制代码
对有doctype申明的页面使用document.documentElement.scrollTop,safari特例独行:使用 window.pageYOffsetdom
function getScrollHeight() {
return Math.max(document.documentElement.scrollTop,window.pageYOffset||0)
}
复制代码
function getDocumentTop() {
return document.documentElement.offsetHeight;
}
复制代码
codepen 触底打印demopost
可视区域
, 滚动高度
,文档高度
的关系,实现最基础的触底加载<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 中。优化功能点以下:优化
为了模拟请求数据,封装了一个 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 发起请求拿取服务端的数据ui
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样式的方法。
<template>
<div id="loading-alert">
<i class="el-icon-loading"></i>
<span>{{ message }}</span>
</div>
</template>
<script>
export default {
props:{
message:{
type:String,
default:'正在加载更多数据'
}
},
};
复制代码
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 的版本。