【uniapp 组件篇】实现图片懒加载附源代码

本文详细介绍了Vue.js中实现图片懒加载的原理和关键步骤,包括监听元素位置、过渡动画和图片链接变化的处理。通过创建Intersection Observer监听图片进入视口,结合过渡效果和事件触发,实现高效且平滑的图片加载体验。同时,文章提供了完整的组件代码示例,以及使用方法和注意事项,适用于优化网页性能和用户体验。
摘要由CSDN通过智能技术生成


不喜欢废话文学,直接上干货!

一、图片懒加载原理

关键思路分为以下几点:

  • 监听元素的在屏幕中的位置,并设置图片到屏幕哪个位置的时候开始加载
  • 根据图片状态显示相应的图片(加载中、加载成功、加载失败
  • 图片加载完和页面销毁的时候一定要取消监听

二、关键步骤

1. 实现监听

关键代码如下:

let intersectionObserver = uni.createIntersectionObserver(this)
		let load = false
		//当图片加载完的时候取消监听
		this.$once('destroyed', () => {
				intersectionObserver.disconnect()
		})
		//this.showDistance就是图片到屏幕哪个位置的时候开始加载的参数
				intersectionObserver.relativeToViewport(this.showDistance).observe('.muqain-load', (res) => {
					if (!load && res.intersectionRatio == 0) {
						load = true
						return
					}
					this.$emit('show');
					load = true
					this.status = 1
					loadTimer = new Date().getTime()
					intersectionObserver.disconnect()
				})

2. 过渡动画

load() {
// minTimeOut 当图片加载过快(存在缓存)至少显示多少秒加载动画
	if (this.minTimeOut == 0) {
			this.isShow = true
	}else{
			let newTimer = new Date().getTime() - loadTimer
				if (newTimer < this.minTimeOut) {
					setTimeout(() => {
							this.isShow = true
					}, this.minTimeOut - newTimer)
				} else {
						this.isShow = true
				}
			}
				
			setTimeout(()=>{
				this.$emit('showSuccess');
			},this.duration)
},

3. 监听图片链接的变化

watch: {
	//当链接变化重新加载
	src() {
	if (!this.isShow) return
	this.status = 0
	this.isShow = false
	
	//dom更新后再执行
	this.$nextTick(() => {
			this.status = 1
		})
	}
}

三、源代码

<template>
	<view class="muqian-content" :style="{
			width,
			height,
		}">
		<!-- 加载中 -->
		<image :src="loadSrc" class="muqian-image muqain-load" @load="init"  mode="aspectFill" :style="{
				opacity:isShow?'0':'1',
				borderRadius,
				width,
				height,
				transition: `opacity ${duration/1000}s ${effect}`
				}"></image>
				
		<!-- 加载成功 -->
		<image class="muqian-image" @load="load" @error="error" v-if="status==1" :src="src" mode="aspectFill" :style="{
				opacity:isShow?'1':'0',
				borderRadius,
				width,
				height,
				transition: `opacity ${duration/1000}s ${effect}`
				}">
		</image>
		<!-- 加载失败 -->
		<image class="muqian-image" v-if="status==2" :src="errorSrc" mode="aspectFill" :style="{
			opacity:isShow?'1':'0',
			borderRadius,
			width,
			height,
			transition: `opacity ${duration/1000}s ${effect}`
			}">
		</image>
		
	</view>
</template>

<script>
	let loadTimer = null
	import loadingImage from '../../static/loading.png'
	import loadFailImage from '../../static/loadFail.png'
	/**
	 * 懒加载插件
	 * @description 懒加载插件
	 *
	 * @property {String}			borderRadius		图片圆角,必须带尺寸单位
	 * @property {String}			width				图片宽度,必须带尺寸单位(默认100%)
	 * @property {String}			height				图片高度,必须带尺寸单位(默认100%)
	 * @property {String}			src					图片链接,不传的话会一直是加载中状态
	 * @property {String|Number}	minTimeOut			当图片加载过快(存在缓存)至少显示多少秒加载动画
	 * @property {String} 			effect = [linear|ease|ease-in|ease-out|ease-in-out] 过渡效果,可以用cubic-bezier
	 * 	@value linear 		规定以相同速度开始至结束的过渡效果(默认)
	 * 	@value ease  		规定慢速开始,然后变快,然后慢速结束的过渡效果
	 * 	@value ease-in 		规定以慢速开始的过渡效果
	 * 	@value ease-out		规定以慢速结束的过渡效果
	 * 	@value ease-in-out  规定以慢速开始和结束的过渡效果
	 * @property {String|Number}			duration	图片加载成功后的过渡时间,单位毫秒
	 * @property {Object}			showDistance		 当图片到屏幕哪个位置的时候开始加载,单位px,可以是负数 (默认{bottom:0})
	 * @property {String}			loadSrc				加载中显示的图片,输入网络路径或绝对路径
	 * @property {String}			errorSrc			加载失败显示的图片,输入网络路径或绝对路径
	 * @event {Function} show 当图片进入页面触发
	 * @event {Function} showSuccess 当图片完全加载完毕触发
	 * @example <muqian-lazyLoad :src="src" width="100rpx" height="100rpx"></muqian-lazyLoad>
	 */

	export default {
		name: "muqian-lazyLoad",
		props: {
			//图片圆角 必须带尺寸单位
			borderRadius: {
				type: String,
				default: '0'
			},
			//图片宽度
			width: {
				type: String,
				default: '100%'
			},
			height: {
				type: String,
				default: '100%'
			},
			//图片链接
			src: {
				type: String,
				default: ''
			},
			//当图片加载过快(存在缓存)至少显示多少秒加载动画
			minTimeOut: {
				type: String || Number,
				default: '300'
			},
			//当图片到屏幕哪个位置的时候开始加载 单位px 可以是负数
			showDistance: {
				type: Object,
				default: () => {
					bottom: 20
				}
			},
			//过渡效果  linear / ease / ease-in / ease-out / ease-in-out
			effect: {
				type: String,
				default: 'linear'
			},
			//图片加载成功后的过渡时间 单位毫秒
			duration: {
				type: String || Number,
				default: '300'
			},
			//加载中图片
			loadSrc: {
				type: String,
				default: loadingImage
			},
			//加载失败图片
			errorSrc: {
				type: String,
				default:loadFailImage
			}

		},
		data() {
			return {
				status: 0, //0加载中 1加载成功 2加载失败
				isShow: false
			}
		},
		watch: {
			//当链接变化重新加载
			src() {
				if (!this.isShow) return
				this.status = 0
				this.isShow = false
				this.$nextTick(() => {
					this.status = 1
				})
			}
		},
		destroyed() {
			//页面销毁取消监听
			this.$emit('destroyed')
		},
		methods: {
			load() {
				if (this.minTimeOut == 0) {
					this.isShow = true
				}else{
					let newTimer = new Date().getTime() - loadTimer
					if (newTimer < this.minTimeOut) {
						setTimeout(() => {
							this.isShow = true
						}, this.minTimeOut - newTimer)
					} else {
						this.isShow = true
					}
				}
				
				setTimeout(()=>{
					this.$emit('showSuccess');
				},this.duration)
			},
			error() {
				this.status = 2
				this.isShow = true
			},
			init(){
				let intersectionObserver = uni.createIntersectionObserver(this)
				let load = false
				//当图片加载完的时候取消监听
				this.$once('destroyed', () => {
					intersectionObserver.disconnect()
				})
				intersectionObserver.relativeToViewport(this.showDistance).observe('.muqain-load', (res) => {
					if (!load && res.intersectionRatio == 0) {
						load = true
						return
					}
					this.$emit('show');
					load = true
					this.status = 1
					loadTimer = new Date().getTime()
					intersectionObserver.disconnect()
				})
			}
		}

	}
</script>

<style lang="scss" scoped>
	.muqian-content {
		overflow: hidden;
		position: relative;
		.muqian-image {
			display: block;
			will-change: transform;
		}

		.muqain-load {
			position: absolute;
			left: 0;
			top: 0;
		}
	}
</style>

使用方法

// 示例
<template>
	<lazyLoad src="图片链接" width="100rpx" height="100rpx" borderRadius="50%"></lazyLoad>
</template>

<script>
//引入组件
import lazyLoad from 'XXX.lazyLoad.vue'

export default {
		components: {
			lazyLoad
		},
		...
}
</script>

注意事项:

  • 组件事件
    show:当图片进入页面触发;
    showSuccess:当图片完全加载完毕触发。

  • 组件只支持固定宽高的图片,可以在调用组件的父组件实现动态宽高传递

  • 加载中和加载失败的图片最佳尺寸为400*400

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭式程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值