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

预览图

请添加图片描述

组件【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>

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

白话机器3:PCA与SVM详细数学原理

一、PCA数学原理 1.数据标准化 首先&#xff0c;需要对原始数据进行标准化处理&#xff0c;使得每个特征的均值为0&#xff0c;方差为1。假设有一个的数据矩阵X&#xff0c;其中每一列是一个样本&#xff0c;每一行是一个特征。 标准化公式如下&#xff1a; 其中&#xff0c;…

Observability:监控与可观察性不同的 3 个原因

作者&#xff1a;来自 Elastic Elastic Observability Team 监控和可观察性通常可以互换使用&#xff0c;但它们并不完全相同。 监控是可观察性的重要组成部分&#xff0c;但可观察性远远超出了传统监控实践的范围。 主要区别&#xff1a;监控从各个组件收集数据 —— 时间和内…

【北京迅为】《iTOP-3588开发板快速烧写手册》-第8章 TF启动

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

PyQt:进度条实现(下载、复制)实时进度显示

一、实现思路 源文件:①被复制的文件&#xff08;一般在客户端自身PC上&#xff09;&#xff1b;②被下载的文件&#xff1b;&#xff08;一般在服务器上&#xff09;。 缓存文件&#xff1a;正在粘贴/下载获取中的文件&#xff0c;粘贴/下载完成前&#xff0c;一般是不完整的…

什么是CE认证?

目录 一、什么是CE认证&#xff1f; 二、CE认证对于企业来说有什么重要性&#xff1f; 三、企业在申请CE认证时&#xff0c;需要满足哪些条件和要求&#xff1f; 一、什么是CE认证&#xff1f; CE认证&#xff0c;即只限于产品不危及人类、动物和货品的安全方面的基本安全要…

鸿蒙内核源码分析(信号消费篇) | 谁让CPU连续四次换栈运行

本篇有相当的难度&#xff0c;涉及用户栈和内核栈的两轮切换&#xff0c;CPU四次换栈&#xff0c;寄存器改值&#xff0c;将围绕下图来说明. 解读 为本篇理解方便&#xff0c;把图做简化标签说明: user:用户空间kernel:内核空间source(…):源函数sighandle(…):信号处理函数&a…

炫酷Chrome:插件大礼包

Chrome浏览器以其强大的功能和丰富的扩展插件库而闻名。 其中&#xff0c;有些插件专为提升用户的浏览体验而设计&#xff0c;例如更换Chrome网页背景图、自定义鼠标点击样式&#xff0c;以及提供便捷的页面跳转工具等。 最近&#xff0c;有一款被称为“宝藏插件包”的工具引…

【软考】模拟考卷错题本2024-05-07

1 项目路径 这里的图没有加载出来&#xff0c;没u哦i关系了。其实主要是的算出最长的路径中包含那些元素即可。这里是蒙圈了&#xff0c;没有考虑到还有更长的。要顾头也顾尾。 2 算法分析-贪心 该问题主要考核的是算法设计策略来达到目标的方式。主要的设计策略有&#xff1a;…

文件加密软件排行榜前四名|好用的四款文件加密软件分享

在数据泄露事件频发的今天&#xff0c;文件加密软件成为了保护个人隐私与企业信息安全的必备工具。 选择一款高效、可靠且易用的加密软件至关重要。 本文精选了当前市场上备受好评的十款文件加密软件&#xff0c;旨在为您在数据保护之旅中提供方向。 1.域智盾 域智盾软件是一…

智慧养老解决方案

PART 1 行业背景及发展趋势 数字看中国人口老龄化 第七次全国人口普查数据显示&#xff0c;我国老年人口总量高达2.64亿人&#xff0c;其中60岁以上人群占比提高至18.7&#xff05;&#xff0c;65岁以上人群占比提高至13.5&#xff05;。 据统计&#xff0c;到2050年&#…

为 Flutter 应用设置主题:ThemeData 和 ColorScheme 指南

在媒体和其他来源中有许多关于这个主题的文章&#xff0c;那么这篇文章的必要性是什么&#xff1f; 在本文中&#xff0c;我计划仅关注 ThemeData 小部件的关键点以及我的开发经验中最常用的参数&#xff0c;并且您将获得有关每个参数如何对您的应用程序执行操作的简要说明。 …

2023年谷歌拒了228万应用,禁了33.3万账号,开发者们应如何应对2024的挑战?

谷歌在上周一公布了去年如何应对恶意应用和恶意行为。 报告指出&#xff0c;去年谷歌在Google Play平台上&#xff0c;通过不断升级安全系统、更新政策规定、运用先进的机器学习技术&#xff0c;以及严格把关应用审核流程&#xff0c;成功阻止了高达228万个不合规的应用程序上架…

人工智能|推荐系统——工业界的推荐系统之重排

一、相似性的度量 基于物品属性标签 基于物品向量表征 ⽤召回的双塔模型学到的物品向量&#xff08;不好&#xff09; 基于内容的向量表征&#xff08;好&#xff09; 二、Maximal Marginal Relevance (MMR) 三、重排的规则 最多连续出现&#x1d458; 篇某种笔记 每&#x…

js如何控制一次只加载一张图片,加载完成后再加载下一张

公众号&#xff1a;程序员白特&#xff0c;欢迎一起交流学习~ 原文&#xff1a;https://juejin.cn/post/7340167256267391012 今天看到一个面试题&#xff0c;是关于img图片加载方面的&#xff0c;有必要记录一下。其实关于这个问题&#xff0c;只要知道图片什么时候加载完成就…

(自适应手机端)物流运输快递仓储网站模板 - 带三级栏目

(自适应手机端)物流运输快递仓储网站模板 - 带三级栏目PbootCMS内核开发的网站模板&#xff0c;该模板适用于物流运输网站、仓储货运网站等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#xff1b;自适应手机端&#xff0c;同一个后台…

3D模型实时变形算法

最近&#xff0c;在尝试渲染一些奇怪的形状后&#xff0c;我陷入了计算机图形学的困境。事实证明&#xff0c;对于我试图解决的具体问题&#xff0c;没有现有的选项完全适合我想要做的事情。几周后&#xff0c;我终于带着一些答案再次浮出水面&#xff0c;写了很多行代码&#…

3.yolov5训练前的图片处理详解(python)

其实&#xff0c;yolov5模型可以分为深度网络、数据处理&#xff08;图片处理&#xff09;、损失函数、优化器选择、训练和预测及部分构成&#xff0c;相信大家对训练和预测的代码比较熟悉。前面两章我们根据代码和结构图了解了yolov5的深度网络&#xff0c;接下来看数据处理的…

Spring中FactoryBean的作用和实现原理

Spring中FactoryBean的作用和实现原理 BeanFactory与FactoryBean&#xff0c;相信很多刚翻看Spring源码的同学跟我一样很好奇这俩货怎么长得这么像&#xff0c;分别都是干啥用的。 BeanFactory是Spring中Bean工厂的顶层接口&#xff0c;也是我们常说的SpringIOC容器&#xff…

Android广播机制简介

文章目录 Android广播机制简介广播的基本概念广播的类型广播的使用场景Android广播的优缺点优点缺点 使用Android广播的一些最佳实践: Android广播机制简介 Android广播是一种轻量级的消息传递机制&#xff0c;用于应用程序之间或系统与应用程序之间进行通信。它类似于订阅-发…

ENG-2 AM,129423-53-6主要用于检测生物体系中的Na+浓度

引言&#xff1a;在化学研究的海洋中&#xff0c;优质的化学试剂是实验成功的关键。今天&#xff0c;我要为大家分享一款备受好评的化学试剂——ENG-2。这款试剂以其独特的性能和广泛的应用领域&#xff0c;赢得了众多科研人员的青睐。 中文名称&#xff1a;钠离子荧光探针ENG-…