uniapp 小程序图片懒加载组件 ImageLazyLoad

news2024/12/26 3:37:47

预览图

请添加图片描述

组件【ImageLazyLoad】代码

<template>
    <view
        class="image-lazy-load"
        :style="{
            opacity: opacity,
            borderRadius: borderRadius + 'rpx',
            background: background,
            transition: `opacity ${time / 1000}s ease-in-out`,
        }"
        :class="'image-lazy-load-item-' + elIndex"
    >
        <view :class="'image-lazy-load-item-' + elIndex" style="height: 100%">
            <image
                :style="{ borderRadius: borderRadius + 'rpx', height: imgHeight, width: imgWidth }"
                v-if="!isError"
                class="image-lazy-load-item"
                :src="isShow ? imageUrl : loadingImg"
                :mode="imgMode"
                @load="imgLoaded"
                @error="loadError"
                @tap="clickImg"
            ></image>
            <!-- 
                1. 空白占位符-因为自己的项目不需要失败时的图片占位,所以这里就用一个div来占位
                2. 如果需要用图片来占位,请注释掉这里代码,把下面那段代码注释去掉就行
             -->
            <view
                v-else
                class="image-lazy-load-item"
                :style="{ borderRadius: borderRadius + 'rpx', height: imgHeight, width: imgWidth, background: background }"
            >
            </view>
            <!-- <image
                :style="{ borderRadius: borderRadius + 'rpx', height: imgHeight, width: imgWidth }"
                class="image-lazy-load-item error"
                v-else
                :src="showErrorImg ? errorImg : ''"
                :mode="imgMode"
                @load="errorImgLoaded"
                @tap="clickImg"
            ></image> -->
        </view>
    </view>
</template>

<script>
/**
 * ImageLazyLoad 图片懒加载组件
 * @description 懒加载使用的场景为:页面有很多图片时,首次加载很慢,导致用户体验感不好.
 * @property {String Number} index 用户自定义值,在事件触发时回调,用以区分是哪个图片,一般用于图片预览
 * @property {String} image 图片路径
 * @property {String} loadingImg 预加载时的占位图
 * @property {String} errorImg 图片加载出错时的占位图
 * @property {String} threshold 触发加载时的位置,见上方说明,单位 rpx(默认300)
 * @property {String Number} duration 图片加载成功时,淡入淡出时间,单位ms(默认)
 * @property {String} effect 图片加载成功时,淡入淡出的css动画效果(默认ease-in-out)
 * @property {Boolean} isEffect 图片加载成功时,是否启用淡入淡出效果(默认true)
 * @property {String Number} borderRadius 图片圆角值,单位rpx(默认0)
 * @property {String Number} width 图片宽度,(默认100%,传值带上单位)
 * @property {String Number} height 图片高度,注意:实际高度可能受imgMode参数影响(默认450rpx 传值带上单位)
 * @property {String Number} imgMode 图片的裁剪模式,详见image组件裁剪模式(默认aspectFill)
 * @property {String} background 占位图片背景色
 * @property {Boolean} showErrorImg 显示加载失败图片(默认false)
 * @event {Function} click 点击图片时触发
 * @event {Function} load 图片加载成功时触发
 * @event {Function} error 图片加载失败时触发
 * @example <ImageLazyLoad :image="image"></ImageLazyLoad>
 */
export default {
    name: "ImageLazyLoad",
    props: {
        index: {
            type: [Number, String],
        },
        // 要显示的图片
        image: {
            type: String,
            default: "",
        },
        // 图片裁剪模式
        imgMode: {
            type: String,
            default: "aspectFill",
        },
        // 占位图片路径
        loadingImg: {
            type: String,
            default: "",
        },
        // 加载失败的错误占位图
        errorImg: {
            type: String,
            default: "",
        },
        // 图片进入可见区域前多少像素时,单位rpx,开始加载图片
        // 负数为图片超出屏幕底部多少距离后触发懒加载,正数为图片顶部距离屏幕底部多少距离时触发(图片还没出现在屏幕上)
        threshold: {
            type: [Number, String],
            default: 300,
        },
        // 淡入淡出动画的过渡时间
        duration: {
            type: [Number, String],
            default: 300,
        },
        // 渡效果的速度曲线,各个之间差别不大,因为这是淡入淡出,且时间很短,不是那些变形或者移动的情况,会明显
        // linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier(n,n,n,n);
        effect: {
            type: String,
            default: "ease-in-out",
        },
        // 是否使用过渡效果
        isEffect: {
            type: Boolean,
            default: true,
        },
        // 圆角值
        borderRadius: {
            type: [Number, String],
            default: 0,
        },
        // 图片宽度,单位rpx
        width: {
            type: [Number, String],
            default: "100%",
        },
        // 图片高度,单位rpx
        height: {
            type: [Number, String],
            default: "450rpx",
        },
        // 占位背景色
        background: {
            type: String,
            default: "",
        },
        // 显示错误图片
        showErrorImg: {
            type: Boolean,
            default: false,
        },
    },
    data() {
        return {
            imageUrl: this.image, // 正在要显示的图片路径
            isShow: false,
            opacity: 1,
            time: this.duration,
            loadStatus: "", // 默认是懒加载中的状态
            isError: false, // 图片加载失败
            elIndex: this.generateRandomString(32),
            isConnected: true, // 网络是否连接 默认连接
        };
    },
    computed: {
        // 将threshold从rpx转为px
        getThreshold() {
            // 先取绝对值,因为threshold可能是负数,最后根据this.threshold是正数或者负数,重新还原
            let thresholdPx = uni.upx2px(Math.abs(this.threshold));
            return this.threshold < 0 ? -thresholdPx : thresholdPx;
        },
        // 计算图片的高度,可能为auto,带%,或者直接数值
        imgHeight() {
            return this.height;
        },
        imgWidth() {
            return this.width;
        },
    },
    created() {
        // 由于一些特殊原因,不能将此变量放到data中定义
        this.observer = {};
    },
    watch: {
        isShow(nVal) {
            // 如果是不开启过渡效果,直接返回
            if (!this.isEffect) return;
            this.time = 0;
            // 原来opacity为1(不透明,是为了显示占位图),改成0(透明,意味着该元素显示的是背景颜色,默认的白色),再改成1,是为了获得过渡效果
            this.opacity = 0;
            // 延时30ms,否则在浏览器H5,过渡效果无效
            setTimeout(() => {
                this.time = this.duration;
                this.opacity = 1;
            }, 30);
        },
        // 图片路径发生变化时,需要重新标记一些变量,否则会一直卡在某一个状态,比如isError
        image(n) {
            if (!n) {
                // 如果传入null或者'',或者undefined,标记为错误状态
                this.isError = true;
            } else {
                this.init();
                this.isError = false;
            }
        },
        // 监听网络变化, 防止网络断开重连的时候,图片一直加载不出来bug
        isConnected(newVal) {
            if (newVal) {
                this.init();
                this.isError = false;
                // 图片链接加个时间戳,防止加载失败后出现缓存,
                // 导致联网后,刷新页面加载失败的图片不能重新加载,而出现空白的bug
                this.imageUrl = this.image + '?time=' + Date.now()
            }
        },
    },
    emits: ["click", "load"],
    methods: {
        // 用于重新初始化
        init() {
            this.isError = false;
            this.loadStatus = "";
        },
        // 点击图片触发的事件,loadlazy-还是懒加载中状态,loading-图片正在加载,loaded-图片加加载完成
        clickImg() {
            let whichImg = "";
            // 如果isShow为false,意味着图片还没开始加载,点击的只能是最开始的占位图
            if (this.isShow == false) whichImg = "lazyImg";
            // 如果isError为true,意味着图片加载失败,这是只剩下错误的占位图,所以点击的只能是错误占位图
            // 当然,也可以给错误的占位图元素绑定点击事件,看你喜欢~
            else if (this.isError == true) whichImg = "errorImg";
            // 总共三张图片,除了两个占位图,剩下的只能是正常的那张图片了
            else whichImg = "realImg";
            // 只通知当前图片的index
            this.$emit("click", this.index);
        },
        // 图片加载完成事件,可能是加载占位图时触发,也可能是加载真正的图片完成时触发,通过isShow区分
        imgLoaded() {
            // 占位图加载完成
            if (this.loadStatus == "") {
                this.loadStatus = "lazyed";
            }
            // 真正的图片加载完成
            else if (this.loadStatus == "lazyed") {
                this.loadStatus = "loaded";
                this.$emit("load", this.index);
            }
        },
        // 错误的图片加载完成
        errorImgLoaded() {
            this.$emit("error", this.index);
        },
        // 图片加载失败
        loadError() {
            this.isError = true;
        },
        disconnectObserver(observerName) {
            const observer = this[observerName];
            observer && observer.disconnect();
        },
        // 生成一个32位由字母组成的字符串
        generateRandomString(length) {
            let result = "";
            const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
            for (let i = 0; i < length; i++) {
                result += characters.charAt(Math.floor(Math.random() * characters.length));
            }
            return result;
        },
    },
    beforeUnmount() {
        // 销毁页面时,可能还没触发某张很底部的懒加载图片,所以把这个事件给去掉
        //observer.disconnect();
    },
    mounted() {
        // 监听网络变化, 防止网络断开重连的时候,图片一直加载不出来bug
        uni.onNetworkStatusChange((res) => {
            // console.log("网络变化:", res.isConnected);
            this.isConnected = res.isConnected;
        });
        // mounted的时候,不一定挂载了这个元素,延时30ms,否则会报错或者不报错,但是也没有效果
        setTimeout(() => {
            // 这里是组件内获取布局状态,不能用uni.createIntersectionObserver,而必须用this.createIntersectionObserver
            // this.disconnectObserver('contentObserver');
            const contentObserver = uni.createIntersectionObserver(this);
            // 要理解这里怎么计算的,请看这个:
            // https://blog.csdn.net/qq_25324335/article/details/83687695
            contentObserver
                .relativeToViewport({
                    bottom: this.getThreshold,
                })
                .observe(".image-lazy-load-item-" + this.elIndex, (res) => {
                    // console.log("relativeToViewport", res);
                    if (res.intersectionRatio > 0) {
                        // 懒加载状态改变
                        this.isShow = true;
                        // 如果图片已经加载,去掉监听,减少性能的消耗
                        this.disconnectObserver("contentObserver");
                    }
                });
            this.contentObserver = contentObserver;
        }, 30);
    },
};
</script>

<style scoped lang="scss">
.image-lazy-load {
    background-color: #fff;
    overflow: hidden;

    &-item {
        width: 100%;
        transform: transition3d(0, 0, 0);
        // 防止图片加载“闪一下”
        will-change: transform;
        /* #ifndef APP-NVUE */
        display: block;
        /* #endif */
    }
}
</style>

组件使用【组件引入这里就不介绍】

 <ImageLazyLoad
      width="100%"
      height="calc((100vw - 24px - 8px) / 2)"
      :image="item.url"
      threshold="300"
 ></ImageLazyLoad>

说明:根据自己情况,设置对应的宽高就行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1653494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

请大数据把我推荐给正在申请小程序地理位置接口的人

小程序地理位置接口有什么功能&#xff1f; 若提审后被驳回&#xff0c;理由是“当前提审小程序代码包中地理位置相关接口( chooseAddress、getLocation )暂未开通&#xff0c;建议完成接口开通后或移除接口相关内容后再进行后续版本提审”&#xff0c;那么遇到这种情况&#x…

Python程序设计 函数(三)

练习十一 函数 第1关&#xff1a; 一元二次方程的根 定义一个函数qg&#xff0c;输入一元二次方程的系数a,b,c 当判别式大于0&#xff0c;返回1和两个根 当判别式等于0&#xff0c;返回0和两个根 当判别式小于0&#xff0c;访问-1和两个根 在主程序中&#xff0c;根据函数返回…

c 双向链表

图片 #include <stdio.h> #include <stdlib.h> #include <string.h>int main(void){ struct film{char name[20];int id;struct film *pre; //前向指针struct film *next; //后向指针 };struct film *headNULL;struct film *ls,*lspre,*work;in…

微信小程序16: 组件通信

父子组件之间的通信 父子组件通信一共有三种方式 属性绑定 用于父组件向子组件的指定属性设置数据&#xff0c;仅能设置JSON兼容的数据 事件绑定 用于子组件向父组件传递数据&#xff0c;可以传递任意数据 获取组件实例 父组件还可以通过this.selectComponent()获取子组件的实…

【C++】:模板初阶

目录 一&#xff0c;泛型编程二&#xff0c;函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的原理2.4 函数模板的实例化2.5 模板参数的匹配原则 三&#xff0c;类模板3.1 类模板的定义格式3.2 类模板的实例化 一&#xff0c;泛型编程 在C语言中如何实现一个通用的交换…

在Leaflet中点对象使用SVG和Canvas两种模式的对比

目录 前言 一、关于SVG和Canvas 1、SVG知识 2、Canvas知识 3、优缺点 二、SVG和Canvas在Leaflet的使用 1、相关类图 2、Leaflet的默认展示方式 三、SVG和Canvas实例及性能对比 1、SVG模式及性能对比 2、Canvas优化 总结 前言 众所周知&#xff0c;在Leaflet当中&#…

我独自升级下载 我独自升级崛起一键下载安装

近期&#xff0c;动画《我独自升级》凭借其高涨的人气&#xff0c;迅速席卷了各大平台&#xff0c;其热度非凡。乘着这股风潮&#xff0c;韩国知名厂商网石集团火速推出了同名游戏力作《我独自升级&#xff1a;ARISE》&#xff0c;让众多粉丝得以跨越次元壁垒&#xff0c;亲自投…

批量图片重命名及汇总

又一堆图片文件需要处理... 源文件分布&#xff1a; 有N个文件夹&#xff0c;每个文件夹下又有M个子文件夹&#xff0c;每个子文件夹下有X张图片。 例如文件夹A下有子文件夹A1,A2,A3&#xff0c;子文件夹A1下有图片a-1,a-2,a-3...... 处理目标&#xff1a; 1、将所有图片汇…

qt开发解压缩zip文件实现

作者开发环境&#xff1a;Qt5.8 &#xff0c;win10 总体思路&#xff1a;首先我们编译zip源码&#xff0c;生成zip的动态库&#xff1b;然后再编译quazip源码&#xff0c;得到quazip的动态库&#xff1b;最后在我们的程序中去调用。 详细步骤&#xff1a; 1、编译zlib zlib…

HackMyVM-Slowman

目录 信息收集 arp nmap whatweb WEB web信息收集 gobuster FTP匿名登录 hydra mysql爆破 mysql登录 fcrackzip爆破 hashcat爆破 ssh登录 提权 系统信息收集 python Capabilities提权 信息收集 arp ┌──(root㉿0x00)-[~/HackMyVM] └─# arp-scan -l Interf…

有哪些渠道找到海外代理IP服务?

在今天的全球化时代&#xff0c;许多企业和个人都需要跨越国界&#xff0c;与世界各地的资源、信息和市场进行连接。海外代理IP服务成跨境在线业务增效的重要的工具&#xff0c;可以帮助拓展业务宽度&#xff0c;以实现更多样化的业务需求。但是&#xff0c;如何找到合适、安全…

华为数据之道第四部分导读

目录 导读 第四部分 第10章 未来已来&#xff1a;数据成为企业核心竞争力 数据&#xff1a;新的生产要素 数据被列为生产要素&#xff1a;制度层面的肯定 数据将进入企业的资产负债表 数据资产的价值由市场决定 大规模数据交互的企业数据生态 数据生态离不开底层技术的…

STM32使用ADC单/多通道检测数据

文章目录 1. STM32单片机ADC功能详解 2. AD单通道 2.1 初始化 2.2 ADC.c 2.3 ADC.h 2.4 main.c 3. AD多通道 3.1 ADC.c 3.2 ADC.h 3.3 main.c 3.4 完整工程文件 1. STM32单片机ADC功能详解 STM32单片机ADC功能详解 2. AD单通道 这个代码实现通过ADC功能采集三脚电…

回答篇:测试开发高频面试题目

引用之前文章&#xff1a;《测试开发高频面试题目》 https://blog.csdn.net/qq_41214208/article/details/138193469?spm1001.2014.3001.5502 本篇文章是回答篇&#xff08;持续更新中&#xff09; 1. 什么是测试开发以及其在软件开发流程中的作用。 a. 测试开发是指测试人员或…

MySQL LRU算法(冷热数据分离)

背景 MySQL中使用的InnoDB存储引擎采用了一种特别的最近最少使用&#xff08;LRU, Least Recently Used&#xff09;算法来管理其Buffer Pool中的页&#xff08;包括数据页和索引页&#xff09;。Buffer Pool是InnoDB用来缓存数据&#xff0c;以减少磁盘I/O操作的内存区域。正…

Kafk设计篇01(设计动机+持久化)

背景 本篇文章基于最新版本&#xff1a;kafka 3.7&#xff0c;其他版本的设计&#xff0c;请参考官网&#xff1a; https://kafka.apache.org/documentation/设计动机 任何组件都有它存在的必要&#xff0c;必然是要解决某一类问题的。我们来看看kafka设计的初衷如何。 kaf…

ICLR 2024 杰出论文奖揭晓!两篇国内论文获荣誉提名

国际学习表征会议&#xff08; International Conference on Learning Representations&#xff0c;简称ICLR&#xff09;&#xff0c;于5月7日至11日在奥地利维也纳展览会议中心举行。 ICLR与NeurIPS&#xff08;Conference on Neural Information Processing Systems&#x…

[笔试训练](十六)

目录 046:字符串替换 047:神奇数 048:DNA序列 046:字符串替换 字符串替换_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 简单模拟题~ class StringFormat { public:string formatString(string str, int n, vector<char> arg, int m) {strin…

紫外激光打标机适合在哪些材料表面进行标记

紫外激光打标机适合在多种材料表面进行标记&#xff0c;特别是那些对热敏感或者需要高精度、高清晰度标记的材料。以下是一些常见的适用材料&#xff1a; 1. 塑料&#xff1a;紫外激光打标机在塑料材料上表现尤为出色&#xff0c;因为紫外激光的短波长和高能量密度使得它能够在…

Konga域名配置多个路由

云原生API网关-Kong部署与konga基本使用 Nginx server{listen 443 ssl;location / {proxy_pass http://127.0.0.1:8100;}location /openApi {proxy_pass http://172.31.233.35:7100/openApi;} } Kong {"id": "f880b21c-f7e0-43d7-a2a9-221fe86d9231&q…