2020年造的轮子,滚动加载(观察元素)

751 阅读3分钟

背景

记一次造轮子的过程,我想大家都有做滚动加载的需求。用UI组件?自己监听滚动事件?用别人的UI组件,万一有坑呢?传统的监听滚动事件,事件密集,容易造成性能问题。所以在2020年的今天是不是可以来点不一样的~_~

目前有一个新的 IntersectionOberver API,可以自动"观察"元素是否可见,Chrome 51已经支持。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。(大概就是如下图,有个滑块在视口进进出出会被监听)


关于IntersectionObserver

//IntersectionObserver本身是一个构造函数
//传入的是一个callback函数,其中回调函数传入的参数是包含被监听对象信息的数组
new IntersectionObserver((change) => {   
    //do something...
   //change是一个数组,每个都是IntersectionObserverEntry对象
})
1. IntersectionObserver.disconnect()
停止监听工作
2. IntersectionObserver.observe()
开始监听一个目标元素
3. IntersectionObserver.takeRecords()
返回所有被观察对象的数组(IntersectionObserverEntry)
4. IntersectionObserver.unobserve()
停止监听特定目标元素
//IntersectionObserverEntry对象里面的属性(都是只读)
1. boundingClientRect 返回包含目标元素的边界信息的DOMRectReadOnly。边界的计算方式与  Element.getBoundingClientRect() 相同
2. intersectionRatio返回intersectionRect 与 boundingClientRect 的比例值
3. intersectionRect返回一个 DOMRectReadOnly 用来描述根和目标元素的相交区域
4. isVisible返回一个布尔值, 是否在该视口内可见
5. isIntersecting返回一个布尔值, 如果目标元素与交叉区域观察者对象(intersection observer) 的根相交,则返回 true.如果返回 true, 则 IntersectionObserverEntry 描述了变换到交叉时的状态; 如果返回 false, 那么可以由此判断,变换是从交叉状态到非交叉状态
6. rootBounds返回一个 DOMRectReadOnly 用来描述交叉区域观察者(intersection observer)中的根
7. target与根出现相交区域改变的元素 (Element)
8. time返回一个记录从 IntersectionObserver 的时间原点(time origin)到交叉被触发的时间的时间戳(DOMHighResTimeStamp)

实战VUE滚动无限加载组件

<template>  
   <div class="scroll-tool" ref="scroll-tool">    
       <slot></slot>   
       <div class="loadStatus">     
         <span v-show="this.noMore">没数据了~</span>
         <div v-show="!this.noMore&&loading">
            加载中<Icon type="ios-loading" class="loadingAnimate"/>
         </div>    
        </div>
  </div>
</template>
<script>
  export default {  
     name: 'scroll-tool',  
     props:{    
       data: {type: Array,default: () => {}},    
       // 数据已经全部加载    
       noMore: {type: Boolean,default: false},
       loading: {type: Boolean,default: false}
     },  
     data () {    
        return {     
             nowOberver: ''    
        }  
     },  
     watch: { 
        data (value, oldVal) {
          this.$nextTick(() => { 
            // 及时清空
            this.nowOberver && this.nowOberver.disconnect()
            this.nowOberver = new IntersectionObserver((change) => {
              // 目标元素与交叉区域观察者对象相交时,还有数据需要加载时,没有正在加载数据 
              if (change[0].isIntersecting && !this.noMore && !this.loading) { 
                  //change就是包含IntersectionObserverEntry对象的数组
                  // 继续加载           
                  this.$emit('loadMore')         
               }        
             })
           const list = this.$refs['scroll-tool'].children[0].children
           this.nowOberver.observe(list[list.length - 1])
          })    
        }  
     },  
  }
</script>
<style scoped lang="less">
    .scroll-tool{  
      .loadStatus{   
         font-size: 14px;
         display: flex;    
         justify-content: center;   
         align-content: center;    
         padding: 10px 0;  
       }  
       .loadingAnimate{    
          animation: animal 1s infinite linear ;    
          -webkit-animation: animal 1s infinite linear ;    
          -webkit-transform-origin: center center;   
          -ms-transform-origin: center center;    
         transform-origin: center center;  
        }
      }
//loading Icon的无限旋转动画
@keyframes animal 
   {  
     0%{    
          transform: rotate(0deg);    
          -ms-transform: rotate(0deg);    
          -webkit-transform: rotate(0deg);  
     }  
     100%{    
        transform: rotate(360deg);    
        -ms-transform: rotate(360deg);    
        -webkit-transform: rotate(360deg);  
  }
}
</style>

其他运用

大家点外卖的时候,可以观察到美团这个页面做的处理。也可以用这个做哟~~