一、源碼理解基礎之scroll.js
1、1 >>> getScrollEventTarget
export default {
getScrollEventTarget(element, rootParent = window) {
let currentNode = element;
while (currentNode && currentNode.tagName !== 'HTML' && currentNode.tagName !== 'BODY' && currentNode.nodeType === 1 && currentNode !== rootParent) {
const overflowY = this.getComputedStyle(currentNode).overflowY;
if (overflowY === 'scroll' || overflowY === 'auto') {
return currentNode;
}
currentNode = currentNode.parentNode;
}
return rootParent;
}
}
getScrollEventTarget:
這個函數用於找到“目標容器”,即:有滾動條的dom。
1、2>>> getScrollTop
getScrollTop(element) {
return 'scrollTop' in element ? element.scrollTop : element.pageYOffset;
}
getScrollTop:
獲取元素的滾動條高度。
Element.scrollTop
屬性可以獲取或設置一個元素的內容垂直滾動的像素數。
一個元素的 scrollTop
值是這個元素的頂部到它的最頂部可見內容(的頂部)的距離的度量。當一個元素的內容沒有產生垂直方向的滾動條,那麼它的 scrollTop 值爲0。
1、3>>> getElementTop
getElementTop(element) {
return (element === window ? 0 : element.getBoundingClientRect().top) + this.getScrollTop(window);
},
getElementTop:
元素頂部至頁面頂部的距離
getBoundingClientRect
用於獲得頁面中某個元素的左,上,右和下分別相對瀏覽器視窗的位置。
1、4>>> getVisibleHeight
getVisibleHeight(element) {
return element === window ? element.innerHeight : element.getBoundingClientRect().height;
},
getVisibleHeight:
獲取元素高度
二、源碼理解之index.vue
重要源碼部分
methods: {
check() {
if (this.loading || this.finished) {
return;
}
const el = this.$el;
const { scroller } = this;
// scrollerHeight 容器可見高度
const scrollerHeight = utils.getVisibleHeight(scroller);
/* istanbul ignore next */
if (!scrollerHeight || utils.getComputedStyle(el).display === 'none' || el.offsetParent === null) {
return;
}
// scrollTop滾動高度
const scrollTop = utils.getScrollTop(scroller);
const targetBottom = scrollTop + scrollerHeight;
let reachBottom = false;
/* istanbul ignore next */
if (el === scroller) {
reachBottom = scroller.scrollHeight - targetBottom < this.offset;
} else {
const elBottom =
utils.getElementTop(el) -
utils.getElementTop(scroller) +
utils.getVisibleHeight(el);
reachBottom = elBottom - scrollerHeight < this.offset;
}
/* istanbul ignore else */
if (reachBottom) {
this.$emit('input', true);
this.$emit('load');
}
},
handler(bind) {
/* istanbul ignore else */
if (this.binded !== bind) {
this.binded = bind;
(bind ? on : off)(this.scroller, 'scroll', this.check);
}
}
}
最主要的方法是check()
重中之重是這段代碼:
const elBottom =
utils.getElementTop(el) -
utils.getElementTop(scroller) +
utils.getVisibleHeight(el);
reachBottom = elBottom - scrollerHeight < this.offset;
下面我就來解釋下這段代碼的意思:
看圖一:
這裏用a來表示utils.getElementTop(el) a=152 代表dom頂部距離page頂部的距離
b表示utils.getVisibleHeight(el) b=800 代表dom的高度
d表示utils.getElementTop(scroller) 即滾動條的滾動的高度,此時沒有滾動,即d=0
c表示scrollerHeight 即整個容器的高度
this.offset可以自定義設定
再來看圖二:
此時滾動條向下滾動了50
所以現在來看下
a=152
b=800
c=852
d=50
此時有變化的是utils.getElementTop(scroller)
如果我們設定this.offset=10
reachBottom = elBottom - scrollerHeight < this.offset
=a-d+b-c=152-50+800-852=50 < 10
所以 reachBottom=false
所以 reachBottom還未達到觸發滾動至底部加載新內容的條件
如果我們設定this.offset=55
reachBottom = 50 < 55 = true
所以此時可以觸發加載新內容。
具體代碼可以查看https://github.com/youzan/vant/tree/dev/packages/list、https://github.com/youzan/vant/tree/dev/packages/utils