实际效果图
gif图片较大,加载较慢,请耐心等待…
复制代码到项目中,可以直接运行
封装m-image组件
<template>
<div
class="m-image"
:class="[
{showLoading, border},
`m-image-${index}`
]"
:style="{'--bgColor': bgColor}"
@click="$emit('click')">
<image
:show-menu-by-longpress='showMenu'
:class="{showPic}"
:mode="mode"
:src='index === null ? src : url'
lazy-load
@load='showPic = true'
/>
</div>
</template>
<script>
export default {
props: {
// 图片地址
src: {
type: String,
default () {
return ''
}
},
// 图片唯一标识,用于懒加载
index: {
type: [Number, Object, String],
default: null
},
// 是否展示图片加载中动画
showLoading: {
type: Boolean,
default: false
},
// 图片加载前背景色
bgColor: {
type: String,
default: '#F5F7F9'
},
// 1px边框
border: {
type: Boolean,
default: false
},
// 图片展示方式,同小程序image
mode: {
type: String,
default: 'aspectFill'
},
// 长按图片识别小程序码
showMenu: {
type: Boolean,
default: false
},
// 距离底部多少距离开始加载图片
threshold: {
type: Number,
default: 100
}
},
data () {
return {
showPic: false,
url: ''
}
},
computed: {
// 将threshold从rpx转为px
getThreshold () {
// 先取绝对值,因为threshold可能是负数,最后根据this.threshold是正数或者负数,重新还原
let thresholdPx = uni.upx2px(Math.abs(this.threshold))
return this.threshold < 0 ? -thresholdPx : thresholdPx
}
},
mounted () {
// 传了index表示开启图片懒加载
if (this.index !== null) {
this.$nextTick(() => {
// 此uOnReachBottom事件由mixin.js发出,目的是让页面到底时,保证所有图片都进行加载,做到绝对稳定且可靠
uni.$once('onReachBottom', () => {
if (!this.url) this.url = this.src
})
// 这里是组件内获取布局状态,不能用uni.createIntersectionObserver,而必须用this.createIntersectionObserver
this.disconnectObserver('contentObserver')
const contentObserver = uni.createIntersectionObserver(this)
contentObserver.relativeToViewport({
bottom: this.getThreshold
}).observe(`.m-image-${this.index}`, res => {
if (res.intersectionRatio > 0) {
// 懒加载状态改变
this.url = this.src
// 如果图片已经加载,去掉监听,减少性能的消耗
this.disconnectObserver('contentObserver')
}
})
this.contentObserver = contentObserver
})
}
},
methods: {
// 如果图片已经加载,去掉监听,减少性能的消耗
disconnectObserver (observerName) {
const observer = this[observerName]
observer && observer.disconnect()
}
}
}
</script>
<style lang='scss' scoped>
.m-image{
background: #f8f8f8;
font-size: 0;
box-sizing: border-box;
position: relative;
background: #f4f4f4;
overflow: hidden;
&.border {
border: 1rpx solid #f7f8f9;
border-radius: inherit;
&:after {
z-index: 3;
border-radius: inherit;
}
}
image{
position: relative;
z-index: 2;
box-sizing: border-box;
width: 100%;
height: 100%;
border-radius: inherit;
opacity: 0;
transition: opacity ease-in-out .2s;
&.showPic {
opacity: 1;
}
}
&.showLoading::before{
content: '';
display: inline-block;
width: 42rpx;
height: 42rpx;
border-radius: 50%;
border: 1.5px solid #ddd;
border-top-color: transparent;
border-right-color: transparent;
animation: rotating .7s linear infinite;
position: absolute;
left: calc(50% - 21rpx);
top: calc(50% - 21rpx);
z-index: 1;
}
}
</style>
使用方式
main.js全局引入m-image
import Vue from 'vue'
import App from './App'
import store from './store'
// 全局组件
import mImage from './components/m-image'
Vue.component('m-image', mImage)
Vue.config.productionTip = false
new Vue({
mpType: 'app',
store,
...App
}).$mount()
父组件使用
<template>
<div>
<ul>
<li v-for="(url, i) in list" :key="i">
<m-image :src='url' :index='i'/>
</li>
</ul>
</div>
</template>
<script>
const pic1 = 'https://img.pddpic.com/mms-material-img/2021-08-31/e6241145-8f17-4d14-8431-9cf782a6f75e.jpeg.a.jpeg'
const pic2 = 'https://img.pddpic.com/mms-material-img/2021-07-15/a2143526-4dd3-4b0a-a88d-3a0095208622.jpeg.a.jpeg'
export default {
data () {
return {
list: [
pic1, pic2,
pic1, pic2,
pic1, pic2,
pic1, pic2,
pic1, pic2,
pic1, pic2,
pic1, pic2,
pic1, pic2,
pic1, pic2,
pic1, pic2
]
}
}
}
</script>
<style lang='scss' scoped>
ul {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 20rpx;
background: #fff;
li {
display: flex;
justify-content: center;
width: 50%;
margin-bottom: 20rpx;
// ⚠️修改图片颜色需要加/deep/
/deep/ .m-image {
width: 45vw;
height: 400rpx;
border-radius: 4rpx;
}
}
}
</style>
至此,代码就写完啦,考虑不周或者有bug的地方,留言告知我哟😊!