一、源码理解基础之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