uniapp小程序/H5封装图片懒加载+渐变加载组件

实际效果图

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的地方,留言告知我哟😊!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值