<template> <view class="tui-lazyload__box" :style="{backgroundColor:placeholder?'transparent':backgroundColor,width:width,height:height?height:'auto',borderRadius:radius}" @tap="handleClick"> <image class="tui-lazyload__img" :class="{'tui-img__hidden':!placeholder && fadeShow && !show,'tui-img__appear':show && !placeholder && fadeShow}" :style="{height:height,borderRadius:radius}" :src="show?src:placeholder" :mode="height&&height!==true?mode:'widthFix'" :webp="webp" :show-menu-by-longpress="showMenuByLongpress" :draggable="draggable" @load="load" @error="error" :id="elId"> </image> <slot></slot> </view> </template> <script> export default { name: "tui-lazyload-img", emits: ['error', 'load', 'click'], options: { virtualHost: true }, props: { //图片路径 src: { type: String, default: '' }, //占位图路径 placeholder: { type: String, default: '' }, //占位背景色,placeholder有值时失效 backgroundColor: { type: String, default: '#E7E7E7' }, //图片的裁剪模式,参考image组件mode属性 mode: { type: String, default: 'widthFix' }, //图片显示动画效果,无占位图时有效 fadeShow: { type: Boolean, default: true }, //默认不解析 webP 格式,只支持网络资源 微信小程序2.9.0 webp: { type: Boolean, default: false }, //开启长按图片显示识别小程序码菜单 微信小程序2.7.0 showMenuByLongpress: { type: Boolean, default: false }, //鼠标长按是否能拖动图片 仅H5平台 3.1.1+ 有效 draggable: { type: Boolean, default: true }, //图片宽度 width: { type: String, default: '340rpx' }, //图片高度,如果高度设置为auto,mode值需要设置为widthFix height: { type: String, default: '340rpx' }, //图片圆角值,如:10rpx radius: { type: String, default: '0' }, //节点布局区域的下边界,目标节点区域以下 bottom(px) 时,就会触发回调函数 bottom: { type: [Number, String], default: 50 }, //是否停止监听,设置为true时回调函数将不再触发 disconnect: { type: Boolean, default: false }, //图片在列表中的索引值 index: { type: Number, default: 0 } }, data() { let elId = this.unique() + this.index return { show: false, elId: elId }; }, watch: { disconnect(val) { if (val) { this.removeObserver() } } }, created() { this.observer = null; // this.elId = this.unique() + this.index; }, mounted() { this.$nextTick(() => { setTimeout(() => { // #ifndef H5 if (!this.disconnect) { this.initObserver() } else { this.show = true; } // #endif // #ifdef H5 if (!this.disconnect && window.self === window.top) { this.initObserver() } else { this.show = true; } // #endif }, 50) }) }, // #ifndef VUE3 beforeDestroy() { this.removeObserver() }, // #endif // #ifdef VUE3 beforeUnmount() { this.removeObserver() }, // #endif methods: { unique: function(n) { n = n || 6; let rnd = ''; for (let i = 0; i < n; i++) rnd += Math.floor(Math.random() * 10); return 'tui_' + new Date().getTime() + rnd; }, removeObserver() { if (this.observer) { this.observer.disconnect() this.observer = null; } }, initObserver() { if (this.observer || this.show) return; try { let element = this.elId ? `#${this.elId}` : '.tui-lazyload__img'; const observer = uni.createIntersectionObserver(this) observer.relativeToViewport({ bottom: Number(this.bottom) || 50 }).observe(element, (res) => { if (res.intersectionRatio > 0 && !this.show) { this.show = true; this.removeObserver() } }) this.observer = observer } catch (e) { //TODO handle the exception this.show = true; this.removeObserver() } }, error(e) { if (!this.show) return; this.$emit('error', { detail: e.detail, index: this.index }) }, load(e) { if (!this.show) return; this.$emit('load', { detail: e.detail, index: this.index }) }, handleClick(e) { this.$emit('click', { index: this.index }) } } } </script> <style scoped> .tui-lazyload__box { display: inline-flex; position: relative; flex-shrink: 0; } .tui-lazyload__img { width: 100%; display: block; flex-shrink: 0; transition: opacity .3s linear; } .tui-img__hidden { opacity: 0; visibility: hidden; } .tui-img__appear { opacity: 1; visibility: visible; } </style>