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>