【前端】图片懒加载的原理和三种实现方式

news2025/2/25 12:19:43

一. 图片懒加载的目的

大型网站如常用的淘宝,京东等页面,需要展示大量的商品图片信息,如果打开网页时让所有图片一次性加载完成,需要处理很多次网络请求,等待加载时间比较长,用户体验感很差。

有一种常用的解决方式是:随着滚动动态加载,即图片的惰性加载。视图之外的图片默认不加载,随着页面的滚动,图片进入了显示的范围,则触发图片的加载显示。

优点:页面加载速度快,用户体验感更好且节省流量

二. 图片懒加载的原理方法

初始化时,图片标签的src不能是真实的图片地址,也不可以是空地址或者坏地址(会出现图片加载失败的图标)。

  1. 初始化的时候,可以设置图片的src是某一个小型图片。例如一张1px*1px的透明图片。由于所有图片都使用这一张图片,只会发送一次请求,不会增加性能负担。
  2. 将图片的真实路径绑定给一个自定义属性,例如data-url。注意:页面的img元素,如果没有src属性,浏览器就不会发出请求去下载图片
<img  data-url="xxx" src="1px.gif" width="100" height="100"/>
  1. 定义滚动事件,判断元素进入视口,则将src替换为真正的url地址。利用js提取data-url的真实图片地址赋值给src属性

三. 图片懒加载的实现方法

图片懒加载的关键在于获取元素的位置,并判断其是否出现在视口。故有以下三种方式

  • 滚动监听+scrollTop+offsetTop+innerHeight
  • 滚动监听+getBoundingClientRect()
  • intersectionObserve()

3.1 滚动监听+scrollTop+offsetTop+innerHeight

  • scrollTop:指网页元素被滚动条卷去的部分。

  • offsetTop:元素相对父元素的位置

  • innerHeight:当前浏览器窗口的大小。需要注意兼容性问题。

    • IE8及更早版本以前没有提供取得浏览器窗口大小的属性,不过提供了API:document.documentElement.clientHeight/clientWidth:返回元素内容及其内边距所占据的空间大小。
    • IE6中,上述属性必须在标准模式才有效,如果是混杂模式,需要通过document.body.clientWidth 和 document.body. clientHeight 取得相同信息。
var pageWidth = window.innerWidth
var pageHeight = window.innerHeight;  
if (typeof pageWidth != "number"){  
    //pageWidth的值不是数值,说明没有innerwidth属性
    if (document.compatMode == "CSS1Compat"){ //标准模式	
        pageWidth = document.documentElement.clientWidth; 
 		pageHeight = document.documentElement.clientHeight; 
 	} else { //混杂模式
 		pageWidth = document.body.clientWidth; 
 		pageHeight = document.body.clientHeight; 
 	} 
}
  • 三个属性之间的关系如图所示,故当scrollTop+innerHeight > offsetTop,即图片在视口内,否则图片在可视区域外。
    在这里插入图片描述

代码实现

滚动监听完成图片懒加载的简易版本

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        img {
            margin-top:400px;
            width: 250px;
            display: block;
        }
    </style>
</head>
<body>
    <img src="img/1pxImg.png" data-url="img/1.jpg">
    <img src="img/1pxImg.png" data-url="img/2.jpg">
    <img src="img/1pxImg.png" data-url="img/3.jpg">
    <img src="img/1pxImg.png" data-url="img/4.jpg">
    <img src="img/1pxImg.png" data-url="img/5.jpg">

    <script>
        var imgs = document.getElementsByTagName('img')
        scrollFn()
        // 监听滚动事件
        window.onscroll = scrollFn
        function scrollFn() {
            var clietH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
            var scrollTop = document.documentElement.scrollTop || window.pageYOffset ||  document.body.scrollTop;
            console.log(clietH, scrollTop);
            Array.from(imgs).forEach((item) =>{
                let eleTop = item.offsetTop
                // console.log(eleTop)
                let count = scrollTop + clietH - eleTop
                console.log(count)
                // 可设置为>100 查看懒加载效果
                if (count > 0) {
                    //从data-url中取出真实的图片地址赋值给scr
                    item.setAttribute('src', item.getAttribute('data-url')) 
                }
            })
        }
    </script>
</body>
</html>

3.2 滚动监听+getBoundingClientRect()

getBoundingClientRect()

Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。返回一个对象,对象属性包括top,right

rectObject = object.getBoundingClientRect();

API返回一个对象,即rectObject为一个对象,其包含以下属性

  • rectObject.top:元素上边到视窗上边的距离;
  • rectObject.right:元素右边到视窗左边的距离;
  • rectObject.bottom:元素下边到视窗上边的距离;
  • rectObject.left:元素左边到视窗左边的距离;
  • rectObject.width:元素自身的宽度
  • rectObject.height:元素自身的高度

在这里插入图片描述

  • 故当rectObject.top的值处于0-视口高度,则元素处于可视区。即
getBoundingClientRect(ele).top >= 0 && getBoundingClientRect(ele).top <= offsetHeight

代码实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        img {
            margin-top:400px;
            width: 250px;
            display: block;
        }
    </style>
</head>
<body>
    <img src="img/1pxImg.png" data-url="img/1.jpg">
    <img src="img/1pxImg.png" data-url="img/2.jpg">
    <img src="img/1pxImg.png" data-url="img/3.jpg">
    <img src="img/1pxImg.png" data-url="img/4.jpg">
    <img src="img/1pxImg.png" data-url="img/5.jpg">

    <script>
        var imgs = document.getElementsByTagName('img')
        scrollFn()
        // 监听滚动事件
        window.onscroll = scrollFn
        function scrollFn() {
            var clietH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
            Array.from(imgs).forEach((item) =>{
                let ele = item.getBoundingClientRect() 
                console.log(clietH,ele.top)
                // 可以设置为ele.top+200 查看懒加载效果
                if (ele.top > 0 && ele.top < clietH) {
                    //从data-url中取出真实的图片地址赋值给scr
                    item.setAttribute('src', item.getAttribute('data-url')) 
                }
            })
        }
    </script>
</body>
</html>

3.3 intersectionObserve()

intersectionObserve()

新的API,针对元素的可见时间进行监听。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。

var io = new IntersectionObserver(callback, option);

IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选)。

构造函数的返回值是一个观察器实例。实例的observe方法可以指定观察哪个 DOM 节点。

// 开始观察
io.observe(document.getElementById('example'));

// 停止观察
io.unobserve(element);

// 关闭观察器
io.disconnect();

上面代码中,observe的参数是一个 DOM 节点对象。如果要观察多个节点,就要多次调用这个方法。

io.observe(elementA);
io.observe(elementB);

callack参数

目标元素的可见性变化时,就会调用观察器的回调函数callback

一般会触发两次:1.目标元素刚刚进入视口(开始可见),2.完全离开视口(开始不可见)。

callback函数的参数是一个数组,每个成员都是一个IntersectionObserverEntry对象。

IntersectionObserverEntry 对象

提供目标元素的信息,一共有六个属性。

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
  • target:被观察的目标元素,是一个 DOM 节点对象
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时小于等于0

所以可以通过判断intersectionRatio属性是否处于(0,1)来判断元素的可见性

代码实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        img {
            margin-top:400px;
            width: 250px;
            display: block;
        }
    </style>
</head>
<body>
    <img src="img/1pxImg.png" data-url="img/1.jpg">
    <img src="img/1pxImg.png" data-url="img/2.jpg">
    <img src="img/1pxImg.png" data-url="img/3.jpg">
    <img src="img/1pxImg.png" data-url="img/4.jpg">
    <img src="img/1pxImg.png" data-url="img/5.jpg">

    <script>
        var imgs = document.getElementsByTagName('img')
        // 观察器实例
        let io = new IntersectionObserver((entires) =>{
            entires.forEach(item => {
                // 原图片元素
                let oImg = item.target
                if (item.intersectionRatio > 0 && item.intersectionRatio <= 1) {
                    oImg.setAttribute('src', oImg.getAttribute('data-url'))
                } 
            })
        })
       // 给每一个图片设置观察器
        Array.from(imgs).forEach(element => {
            io.observe(element) 
        });
    </script>
</body>
</html>

备注:本文章为学习前端知识过程中的记录和分享,如有错误欢迎指正!

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

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

相关文章

基于 vite 创建 vue3 全家桶项目(vite + vue3 + tsx + pinia)

vite 最近非常火&#xff0c;它是 vue 作者尤大神发布前端构建工具&#xff0c;底层基于 Rollup&#xff0c;无论是启动速度还是热加载速度都非常快。vite 随 vue3 正式版一起发布&#xff0c;刚开始的时候与 vue 绑定在一起&#xff0c;但之后的 v2 版本便比较独立&#xff0c…

什么是垃圾回收机制(超详细)

垃圾回收机制 1.垃圾回收机制&#xff08;Garbage Collction&#xff09;简称GC&#xff0c;是JavaScript中使用的内存管理系统的基本组部分&#xff0c;是为了防止内存泄漏 2.JavaScript是在创建变量&#xff08;对象、字符串等&#xff09;时自动进行了分配内存&#xff0c;并…

Web实训项目--网页设计(附源码)

1 实训基本信息 1.1 实训项目名称 网页设计 1.2 实训环境 本次实训的形式以实战讲解为主&#xff0c;以项目为主导。学习如何设计网页中的轮播图和动画效果&#xff0c;并掌握a标签文本内容跳转、超链接的应用、播放音乐与视频等操作。 2 实训内容简介 2.1 网页设计 通过…

关于vue中如何清除定时器的方法

一、问题 1、在vue中使用setTimeout定时器的时候&#xff0c;可能会遇到关不掉的情况&#xff0c;会存在明明已经在beforeDestroy和destroyed中设置了定时器清除了&#xff0c;但是有时候没生效&#xff0c;定时器还会继续执行。 2、在这里需要说一下setTimeout的使用场景&am…

路由vue-route的使用

文章目录一、项目初始化二、路由配置规则vue-route标签作用&#xff1a;路由匹配到的组件将渲染到这里router-link标签作用&#xff1a;路由导航&#xff08;路由跳转的链接&#xff09;三、声明式导航和编程式导航声明式导航编程式导航四、路由重定向五、嵌套路由特别注意总结…

04-vscode搭建cmake的编译环境

vscodemingw搭建C/C环境系列 01.vscodemingw搭建编译调试环境 02-vscode编译调试单个源文件程序 03-vscode编译调试多个源文件程序 04-vscode搭建cmake的编译环境 文章目录一、简述二、vscode搭建cmake的编译环境&#xff08;方法1&#xff09;1.配置cmake构建目录(可忽略)…

Redux——详解

一.初识Redux 相当于vue中的vuex 1.redux 是一个独立专门用于做状态管理的 JS 库(不是 react 插件库) 2.它可以用在 react, angular, vue 等项目中, 但基本与 react 配合使用 3.作用: 集中式管理 react 应用中多个组件共享的状态 看看Redux的工作原理图 如果要进行加一操作…

ES6---promise详解及用法

一、什么是Promise Promise是ES6异步编程的一种解决方案(目前最先进的解决方案是async和await的搭配&#xff08;ES8&#xff09;&#xff0c;但是它们是基于promise的)&#xff0c;从语法上讲&#xff0c;Promise是一个对象或者说是构造函数&#xff0c;用来封装异步操作并可…

炫酷登录注册界面【超级简单 jQuery+JS+HTML+CSS实现】

一&#xff1a;源码获取 这两天根据需求写了一个比较好看的有动态效果的登录注册切换页面&#xff0c;这里我将源码资源分享给大家&#xff0c;大家可以直接免费下载使用哦&#xff0c;没有 vip 的小伙伴找我私聊发送"登录注册"即可我给你发文件&#xff0c;此登录注…

四、vue中路由router配置详解

目录 一、vue中的路由作用 二、vue中的路由router 使用步骤 三、路由跳转 1、带参数路由跳转&#xff1a; &#xff08;1&#xff09;this.$router.push() &#xff1a; &#xff08;2&#xff09;this.$router.replace() &#xff1a; 2、不带参数路由跳转 3、this.…

【手把手教安装】VUE安装教程+VScode配置!!!

含泪整理Vue安装教程 因为换了新电脑很多软件要重装&#xff0c;所以想到可以写一份教程&#xff0c;为我以后换电脑方便重装也为了大家&#xff01;&#xff01; 第一次安装Vue踩坑太多&#xff0c;这里整理一份超详细教程&#xff08;win11也可&#xff01;&#xff01;&…

uniapp 控制台警告 DevTools failed to load SourceMap: Could not load content for http://127.0.0.1问题解决

在uniapp中控制台警告&#xff0c;这是用于在开发时定位到报错的源码位置的工具SourceMap出问题了&#xff0c;如果单纯只是想消除这个警告的方法写在下面第一个&#xff0c;真正解决工具问题的方法是第二个&#xff08;需要下载一个插件&#xff09;&#xff0c;现在放在这边做…

如何修复运行缓慢的 WordPress 网站?

&#x1f482; 个人网站:【海拥】【摸鱼游戏】【神级源码资源网站】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 就其 SEO 而言&…

npm、nrm两种方式查看源和切换镜像

一、使用npm查看当前源、切换淘宝镜像、切换官方源 &#xff08;1&#xff09;npm查看当前源&#xff1a; npm get registry&#xff08;2&#xff09;npm设置淘宝镜像源&#xff1a; npm config set registry http://registry.npm.taobao.org&#xff08;3&#xff09;npm设…

2023 前端一场面试及答案整理

金三马上就要开始了&#xff0c;俗话说得好&#xff0c;知己知彼百战百胜&#xff0c;多准备总是没有错的。以面试的形式和大家一起学习、一起回顾我们的职场生涯。今天简单总结一下我个人去面试&#xff0c;包括我在面试别人时的经验。加油加加油&#xff01;&#xff01;&…

2022最新Nodejs下载安装配置步骤(保姆级教程)

1. 进入官网选择下载版本 http://nodejs.cn/download/2.安装过程 步骤1&#xff1a;选择next选项 步骤2&#xff1a;勾选接受协议选项&#xff0c;点击 next&#xff08;下一步&#xff09;按钮 : 步骤3&#xff1a;其默认安装目录是C:\Program Files\nodejs\&#xff0c;当…

【Python】ttkbootstrap的介绍与使用

一、什么是ttkbootstrap&#xff1f; 官方文档 [较慢]&#xff1a;https://ttkbootstrap.readthedocs.io/en/latest/ ttkbootstrap 是一个基于 tkinter 的界面美化库&#xff0c;使用这个工具可以开发出类似前端 bootstrap 风格的 tkinter 桌面程序 二、安装步骤 安装命令…

Vue项目安装less和less-loader

第一步&#xff1a;查看webpack和webpack-cli是否安装 打开cmd&#xff0c;通过命令查看 webpack -vwebpack-cli -v如果没有安装&#xff0c;要先进行安装 可以通过 npm view webpack version / npm view webpack-cli version 查看当前webpack的最新版本 可以通过 npm vie…

什么是axios(写给小白的理解笔记)

0.为什么会诞生axios 最初浏览器页面向服务器请求数据时&#xff0c;返回的是整个页面&#xff0c;整个页面都会刷新&#xff0c;当我们只需要请求部分数据时&#xff0c;返回整个页面会造成网络资源的占用&#xff0c;为了提高数据请求效率&#xff0c;异步网络请求Ajax出现了…

羊了个羊网页版

最近羊了个羊火的不得了&#xff0c;利用周末时间实现一个网页版。步骤如下&#xff1a; 1&#xff0c;用reactjs 实现。 2&#xff0c;实现Gameroom类。 3&#xff0c;实现Card类。 4&#xff0c;通过父组件控制子组件通信方式&#xff0c;控制点击事件。 5&#xff0c;通过t…