基于uni-app的图片懒加载

4,582 阅读1分钟

uni-app自带是有图片懒加载,可是对于我们h5端并不支持。且uni-app还没有指令这么一说。故写了一个懒加载组件。

实现思路:找到最近的父容器滚动区域,监听该父容器的滚动事件,并检查该图片是否已在可视区域内,有过在则执行img load的方法。

组件代码如下:

<template>
 <img
   :src="src"
   alt=""
   v-if="imgSrc"
   class="lazy-load"
   :mode="mode"
 >
 <div
   class="lazy-wrapper"
   :style="backgroundSrc ? 'background-image:url('+src+')' : ''"
   v-else
 >
   <slot></slot>
 </div>
</template>

<script>
 import { lazyLoad, scrollParent } from './config-instance.js'
 export default {
   props: ['img-src', 'background-src', 'mode', 'eventBus'],
   data() {
     return {
       src: '',
     }
   },
   mounted() {
     this.src = lazyLoad.config.defaultImg;
     let ele = scrollParent(this.$el) || window
     lazyLoad.changeListenObj(ele)
     lazyLoad.addLazyImg(this)
     lazyLoad.lazyLoadHandler()
     window.lazyLoad = lazyLoad
   },
   methods: {
     checkInView() {
       let rect = this.$el.getBoundingClientRect()
       return (rect.top < window.innerHeight * lazyLoad.config.preLoad && rect.bottom > 0)
     },
     loadImg() {
       return new Promise((resolve, reject) => {
         let img = new Image()
         img.src = this.imgSrc || this.backgroundSrc;
         if (img.complete) {
           this.src = this.imgSrc || this.backgroundSrc;
           return resolve()
         }
         img.onload = () => {
           this.src = this.imgSrc || this.backgroundSrc;
           resolve(this.imgSrc)
         }
         img.onerror = (err) => {
           console.log(err, /err/, this.imgSrc)
           this.src = lazyLoad.config.errorImg;
           reject()
         }
       })
     }
   },
   beforeDestroy() {
     lazyLoad.removeLazyImg(this)
   }
 }
</script>

<style>
</style>

config-instance.js代码如下

import lazyImg from './index.js'
let lazyLoad = new lazyImg({
  preLoad: 1.3,
  defaultImg: '',
  errorImg: 'http://img.wkdao.com/image/65/2020/06/02/a638171b79405f698b215882dd165e3f.jpg'
})

const style = (el, prop) => {
  return typeof getComputedStyle !== 'undefined' ?
    getComputedStyle(el, null).getPropertyValue(prop) :
    el.style[prop]
}

const overflow = (el) => {
  return style(el, 'overflow') + style(el, 'overflow-y') + style(el, 'overflow-x')
}

const scrollParent = (el) => {
  if (!(el instanceof HTMLElement)) {
    return window
  }

  let parent = el

  while (parent) {
    if (parent === document.body || parent === document.documentElement) {
      break
    }
    let tagName = parent.parentNode.tagName.toLocaleLowerCase()
    if (!parent.parentNode || tagName == 'uni-page-body') {
      break
    }
    if (/(scroll|auto)/.test(overflow(parent)) && parent.scrollHeight > parent.clientHeight) {
      return parent
    }

    parent = parent.parentNode
  }

  return window
}
export { lazyLoad, scrollParent }

主方法如下

import { throttle } from '@/util/common.js'
class lazyImg {
  constructor(options) {
    let defaultOptions = {
      preLoad:1,
      defaultImg:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4094017208,2604884334&fm=26&gp=0.jpg',
      errorImg:'http://5b0988e595225.cdn.sohucs.com/images/20171122/dc317931664c494983df538cd3dea230.gif'
    }
    this.config = Object.assign(defaultOptions,options)
    this.listenObj = window;
    this.ListenerQueue = []
    this.ListenerTarget = []
    this.lazyLoadHandler = throttle(this._lazyLoadHandler.bind(this), 300)
  }
  changeListenObj(ele){
    this.listenObj = ele
  }
  _initListen(start) {
    if (start) {
      this.listenObj.addEventListener('scroll', this.lazyLoadHandler)
      return
    }
    this.listenObj.removeEventListener('scroll', this.lazyLoadHandler)
  }
  addLazyImg(vm) {
    this.ListenerQueue.push(vm)
    // 判断是否已经init了监听事件
    if(!this.ListenerTarget.find(item=>item == this.listenObj)){
      this.ListenerTarget.push(this.listenObj)
      this._initListen(true)
    }
  }
  _lazyLoadHandler() {
    this.ListenerQueue.forEach((listener, index) => {
      const catIn = listener.checkInView()
      if (!catIn) return
      listener.loadImg().then(res => {
        this.removeLazyImg(listener)
      }).catch(err=>{})
    })
  }
  removeLazyImg(item) {
    if (!this.ListenerQueue.length) {
      this.ListenerTarget = [];
      this._initListen(false)
      return
    }
    const index = this.ListenerQueue.indexOf(item)
    if (index > -1) return this.ListenerQueue.splice(index, 1)
  }
}
export default lazyImg

使用如下

//正经图片
<lazy-img img-src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4094017208,2604884334&fm=26&gp=0.jpg"></lazy-img>
//背景图片
<lazy-img img-src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4094017208,2604884334&fm=26&gp=0.jpg"></lazy-img>