好用的API-IntersectionObserver
前段时间研究PDF展示的时候,在github上找到了一段写的很好的代码(向大佬致敬),它实现加载更多的时候使用了IntersectionObserver这个API,我觉得挺好的,分享给大家,然后写了一个demo,实现了惰性加载和无限滚动。
如果你不熟悉可以看看这篇文章,里面的内容可能没有及时更新,可搭配MDN食用。先看一下demo效果:
动图太快,我们看静态.png
最开始渲染了,11个li
标签,但是只有在屏幕上显示的5个li
标签才加载了图片,其他在不可见范围内的li
是没有加载图片的,实现了资源的懒加载
,在滚动的过程中,会加载可视范围内li
的图片资源,滚动到底后,会加载更多图片,实现无限滚动
。
这一切的一切全是全部是IntersectionObserver
的功劳。
// APP.vue
<template>
<div id="app">
<ul class="imgs">
<li v-for=" i in wrapMaxCount" :key="i" v-visible.once="loadImg" class="img-item" ></li>
</ul>
<div v-visible = "loadMore" class="observer"></div>
</div>
</template>
<script>
import visible from './directives/visible'
export default {
name: "App",
components: {},
directives: {
visible,
},
data(){
return {
maxCount: 11,
currIndex: 0,
wrapMaxCount: 11
}
},
mounted() {
this.getImg()
},
methods: {
getImg(i){
if(typeof i === 'number')
return require(`./assets/demo${i}.jpg`)
},
loadImg(e){
const index = this.currIndex % this.maxCount
const elImg = document.createElement('img')
elImg.src = this.getImg(index)
e.appendChild(elImg)
this.currIndex += 1
},
loadMore(){
console.log('loadMore')
this.wrapMaxCount += 5
},
fetch(){
},
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
margin-top: 60px;
}
.imgs{
display: flex;
flex-direction: column;
align-items: center;
margin: 1rem auto;
}
.img-item{
width: 31.25rem;
height: 31.25rem;
list-style: none;
}
.img-item img{
width: 100%;
height: 100%;
}
</style>
// directive visible
const observerMap = new WeakMap()
const createObserve = (el,vnode,modifiers,callBack)=>{
const observer = new IntersectionObserver(entries=>{
const entry = entries[0]
if(entry.isIntersecting){
callBack(el)
if( modifiers && modifiers.once){
disconnectObserver(observer)
}
}
})
vnode.context.$nextTick(()=>observer.observe(el))
return observer
}
const disconnectObserver = (observer)=>{
if(observer){
observer.disconnect()
}
}
const bind = (el,{value,modifiers },vnode) => {
if (typeof window.IntersectionObserver === 'undefined') {
throw('IntersectionObserver API is not available in your browser.')
}else{
const observer = createObserve(el,vnode,modifiers,(el)=>{
const callback = value
if(typeof callback === 'function')
callback(el)
})
observerMap.set(el,{observer})
}
}
const update = (el, { value, oldValue,modifiers }, vnode) =>{
if(value === oldValue) return
const {observer} = observerMap.get(el)
disconnectObserver(observer)
bind(el,{value,modifiers},vnode)
}
const unbind = (el)=>{
if(observerMap.has(el)){
const {observer} = observerMap.get(el)
disconnectObserver(observer)
observerMap.delete(el)
}
}
export default {
bind,
update,
unbind
}
实现
懒加载:
IntersectionObserver
提供isIntersecting
方法,检测元素是否出现在可视区域内,如果出现在可视区域内就去执行回调函数。once修饰符确保只执行一次回调,update方法中的 if(value === oldValue) return
判断是为了保证已经渲染过的元素不在重新渲染,value
是你绑定的值,也是需要回调的函数,不要使用()=>doSome()
,会被判定成不相等,会重复渲染。
无限加载:
放一个空白的div
在底部,当出现的时候去执行相应的加载回调函数。