好用的API-IntersectionObserver


好用的API-IntersectionObserver

​ 前段时间研究PDF展示的时候,在github上找到了一段写的很好的代码(向大佬致敬),它实现加载更多的时候使用了IntersectionObserver这个API,我觉得挺好的,分享给大家,然后写了一个demo,实现了惰性加载和无限滚动。

​ 如果你不熟悉可以看看这篇文章,里面的内容可能没有及时更新,可搭配MDN食用。先看一下demo效果:

​ 动图太快,我们看静态.png

​ 最开始渲染了,11li标签,但是只有在屏幕上显示的5li标签才加载了图片,其他在不可见范围内的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在底部,当出现的时候去执行相应的加载回调函数。


文章作者: 木叶勇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 木叶勇 !
 上一篇
JavaScript核心-深浅拷贝 JavaScript核心-深浅拷贝
JavaScript核心-深浅拷贝浅拷贝什么是浅拷贝​ 对于引用对象类型只能复制引用地址 实现方法object.asign()Array.slice()解构运算符(…)Array.concat()自定义实现深拷贝什么是深拷贝实现方法J
下一篇 
组件-toast 组件-toast
一个比较好用的ToastNPM 效果图 document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.d
2021-05-06
  目录