前端图片懒加载的原理与3种实现方式举例

作者:hxxfight 时间:2024-04-17 10:20:02 

一. 图片懒加载的目的

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

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

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

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

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

2.初始化的时候,可以设置图片的src是某一个小型图片。例如一张1px*1px的透明图片。由于所有图片都使用这一张图片,只会发送一次请求,不会增加性能负担。将图片的真实路径绑定给一个自定义属性,例如data-url。注意:页面的img元素,如果没有src属性,浏览器就不会发出请求去下载图片

<img  data-url="xxx" src="1px.gif" width="100" height="100"/>

3.定义滚动事件,判断元素进入视口,则将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:元素自身的高度

前端图片懒加载的原理与3种实现方式举例

故当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:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为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>

来源:https://blog.csdn.net/qq_44947815/article/details/125286969

标签:前端,图片,懒加载
0
投稿

猜你喜欢

  • cropper js基于vue的图片裁剪上传功能的实现代码

    2024-05-11 09:10:50
  • thinkphp在模型中自动完成session赋值示例代码

    2024-05-03 15:51:32
  • vscode通过Remote SSH远程连接及离线配置的方法

    2022-01-09 00:31:08
  • 兼容IE,FF的弹出层登陆界面代码

    2008-01-04 12:13:00
  • 对python修改xml文件的节点值方法详解

    2021-02-21 19:54:43
  • CentOS 7.2 Yum编译安装MySQL 5.6

    2024-01-19 06:03:18
  • Python入门教程2. 字符串基本操作【运算、格式化输出、常用函数】 <font color=red>原创</font>

    2021-08-21 17:17:21
  • MySQL的Query Cache原理分析

    2024-01-25 05:20:29
  • 重新认识CSS的权重

    2011-05-24 17:06:00
  • Python实现把回车符\\r\\n转换成\\n

    2022-09-21 07:22:14
  • python多线程超详细详解

    2023-08-09 09:10:23
  • Python使用百度翻译开发平台实现英文翻译为中文功能示例

    2021-10-22 14:56:27
  • Java API学习教程之正则表达式详解

    2023-10-23 05:28:21
  • Python 实现定积分与二重定积分的操作

    2023-08-05 18:22:27
  • Python入门教程(九)Python字符串介绍

    2023-02-05 22:34:43
  • [译]2009年海外Web设计风潮(上)

    2009-01-23 09:21:00
  • bootstrap-table formatter 使用vue组件的方法

    2024-04-29 13:12:37
  • mysql时间相减如何获取秒值

    2024-01-19 17:04:10
  • node实现socket链接与GPRS进行通信的方法

    2024-05-13 09:26:40
  • python实现批量监控网站

    2023-07-22 20:41:08
  • asp之家 网络编程 m.aspxhome.com