likes
comments
collection
share

JS图片懒加载(详解注释+功能解析)

作者站长头像
站长
· 阅读数 56

一、前言

懒加载原理: 图片进入可视区域之后再去请求图片资源

实现原理:

  • 1.使用img标签,scr属性初始为空
  • 2.写一个自定义属性字段,该属性的值写成图片地址
  • 3.当图片在可视区域的范围内的时候,将自定义属性的值作为src的值

图片是在img标签有src属性才去加载的。

  • 懒加载的原理就是在图片进入可视化区域之后,动态的去设置img标签中的src属性。如果在可视区域之外的图片的img中的src属性已被提前设置,那么就不是懒加载了,而是所有图片都会被加载。
  • 在图片进入可视化区域之前,img标签上的src属性并没有被设置。在img标签上有一个data-url属性。该属性的值存放着图片的真实请求地址,但是由于src属性上并没有值,所以图片并不会被加载。我们的js可以监听scroll事件,在计算到图片进入可视区域之后。就拿到img标签上的data-url属性的值,把它设置到src属性上,这就是懒加载的一个原理。

图片懒加载的优势:

  • 对于电商等图片很多,页面很长的业务场景适用
  • 减少无效资源的加载:比如我们的页面有100张图片,但是用户往下滑动看的时候只看了十张图片。如果我们把这100张图片全部都加载完成,页面上会有很多的浪费。所以就可以使用懒加载,图片进入可视区域之后再去加载。
  • 并发加载的资源过多会阻塞js的加载,影响网站的正常使用。因为浏览器会限制在一个域名下并发请求的数量。如果前边图片加载数量过多,要发送http请求,就会影响到后边js的并发加载,影响到页面中js逻辑的使用(JS文件一般放在页面底部)。使用图片懒加载可以避免这个问题。

二、代码注释详解

<!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>
</head>
<style>
    .box {
        width: 400px;
        margin: 0 auto;
    }

    .box img {
        display: block;
        width: 100%;
        height: 200px;
    }
</style>

<body>
    <div class="box">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/夕阳.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/沙滩.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/湖泊.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/雪山.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/夕阳.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/沙滩.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/湖泊.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/雪山.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/夕阳.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/沙滩.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/湖泊.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/雪山.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/夕阳.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/沙滩.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/湖泊.jpg">
        <img src="./imgs/加载中.jpg" alt="" class="lazyload-img" data-src="./imgs/雪山.jpg">
    </div>
    <script>
        // 获取根元素节点
        let viewport = document.documentElement;
        // 把DOM节点数组转为真正的数组,即伪数组转数组
        // let imgArr = [].slice.call(document.querySelectorAll('.lazyload-img'));
        let imgArr = Array.from(document.querySelectorAll('.lazyload-img'))
        // 该函数用来判断某一个元素是否在可视区域
        function isVisible(element) {
            // 获取当前元素节点的大小、位置等信息
            let rect = element.getBoundingClientRect();
            // 用户不管是从上向下、从下向上、从左向右、从右向左滑动,都可以判断当前元素是否在可视区域
            return rect.top < viewport.clientHeight && rect.bottom > 0 && rect.left < viewport.clientWidth && rect.right > 0;
        }
        //防抖处理
        let timer = null;
        // 该函数动态设置图片的src属性,动态加载图片
        function lazyloadImg() {
            for (let i = 0; i < imgArr.length; i++) {
                let img = imgArr[i];
                // 如果元素在可视区域内
                if (isVisible(img)) {
                    // 则把data-src属性赋值给data-src
                    img.src = img.getAttribute('data-src');
                    // 当前元素从未加载数组中移除
                    imgArr.splice(i, 1);
                    // 后一下标前移至当前下标,自减再循环
                    i--;
                }
            }
            // 可视区域内加载完成后,防抖限制解除
            timer = null;
        }
        // 一开始先执行一次,把当前时间在可视区域的图片加载出来
        lazyloadImg();

        // 监听页面滚动
        document.addEventListener('scroll', function () {
            // 如果已触发滚动期间再触发
            console.log(timer);
            if (timer) {
                // 则清除现有执行
               return clearTimeout(timer);
            }
            // 重新赋值
            timer = setTimeout(() => {
                // 页面滚动的时候,不断有新的图片进入可视区域,此时再调用lazyloadImg函数
                lazyloadImg();
            }, 100);
        })
        console.log(timer);

    </script>
</body>

</html>

三、功能解析

1、document.documentElement

document.documentElement 属性返回当前文档的根元素节点。HTML网页的该属性一般是<html>节点。

document.documentElement的clientHeight属性,返回当前视口的高度(即浏览器窗口的高度,不包括滚动条)。

document.documentElement的clientWidth属性,返回当前视口的宽度(即浏览器窗口的宽度,不包括滚动条)。

另外,window.innerHeight 和 window.innerWidth 属性,也会返回网页在当前窗口中可见部分的高度和宽度,即“视口”(viewport)的大小(单位像素)。但是这两个属性值包括滚动条的高度和宽度。

2、Array.from() 或 [].slice.call()

  • 即伪数组转数组

3、getBoundingClientRect()

getBoundingClientRect方法(所有的元素结点上都有这个方法)返回一个对象,提供当前元素节点的大小、位置等信息,基本上就是 CSS 盒模型的所有信息。

let rect = obj.getBoundingClientRect();

getBoundingClientRect方法返回的rect对象,具有以下属性:

  • x:元素左上角相对于视口的横坐标
  • y:元素左上角相对于视口的纵坐标
  • height:元素高度
  • width:元素宽度
  • left:元素左上角相对于视口的横坐标,与x属性相等
  • right:元素右边界相对于视口的横坐标(等于x + width)
  • top:元素顶部相对于视口的纵坐标,与y属性相等
  • bottom:元素底部相对于视口的纵坐标(等于y + height) JS图片懒加载(详解注释+功能解析) 由于元素相对于视口(viewport)的位置,会随着页面滚动变化,因此表示位置的四个属性值,都不是固定不变的。

注意,getBoundingClientRect方法的所有属性,都把边框(border属性)算作元素的一部分。也就是说,都是从边框外缘的各个点来计算。因此,width和height包括了元素本身 + padding + border

转载自:https://juejin.cn/post/7238479969180074045
评论
请登录