來看下幾張圖: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 的版本。