194 lines
4.1 KiB
Vue
194 lines
4.1 KiB
Vue
<template>
|
|
<view class="tui-lazyload__box"
|
|
:style="{backgroundColor:placeholder?'transparent':backgroundColor,width:width,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}" :id="uid" :src="show?src:placeholder" :mode="mode" :webp="webp"
|
|
:show-menu-by-longpress="showMenuByLongpress" :draggable="draggable" @load="load" @error="error">
|
|
</image>
|
|
<slot></slot>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: "tui-lazyload-img",
|
|
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() {
|
|
return {
|
|
show: false,
|
|
observer: null,
|
|
uid: null
|
|
};
|
|
},
|
|
watch: {
|
|
disconnect(val) {
|
|
if (val) {
|
|
this.removeObserver()
|
|
}
|
|
}
|
|
},
|
|
created() {
|
|
this.uid = this.unique() + this.index;
|
|
},
|
|
mounted() {
|
|
if (!this.disconnect) {
|
|
this.initObserver()
|
|
} else {
|
|
this.show = true;
|
|
}
|
|
},
|
|
beforeDestroy() {
|
|
this.removeObserver()
|
|
},
|
|
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.uid ? `#${this.uid}` : '.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-block;
|
|
position: relative;
|
|
}
|
|
|
|
.tui-lazyload__img {
|
|
width: 100%;
|
|
display: block;
|
|
flex-shrink: 0;
|
|
transition: opacity .3s linear;
|
|
}
|
|
|
|
.tui-img__hidden {
|
|
opacity: 0;
|
|
}
|
|
|
|
.tui-img__appear {
|
|
opacity: 1;
|
|
}
|
|
</style>
|