图片懒加载是前端开发过程中非常常见的一个功能。图片懒加载本质上是通过检测可见区域来判断是否发送网络请求,从而优化网页加载速度,本文将实现图片懒加载功能。

1. 效果预览

20220515_005437

2. 实现思路

对于图片懒加载,实际的实现思路实际上就是当图片处于或即将处于可视窗口中时,网页发送网络请求,从而加载图片。

之所以不一开始就加载图片,是因为大量图片同一时间加载,会造成浏览器页面的卡顿,所以才有了懒加载的需求。

所以懒加载的核心就是

const windowHeight = window.innerHeight;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const offsetTop = imgContainer.offsetTop;
(windowHeight + scrollTop) > offsetTop;

即窗口高度+滚动高度>图片距离顶部的高度

但实际使用过程中,为了让用户有更好的体验,上面的逻辑通常是下面这个样子

(windowHeight + scrollTop +256) > offsetTop;

这里可以是256,也可以是更大或更小的数值,同样也是实现懒加载,但是由于多加了一点距离,这样就保证了大多数时候,用户总能及时看到图片,而不是看到占位的背景切换成图片。

3. 实现代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>

        .container {
            width: max-content;
            height: 400px;
        }

        /*=========要加载的图片添加lazy class===========*/
        /*根据自己需要,修改图片加载前的颜色,或者用展位图代替*/
        .lazy {
            width: 100%;
            height: 100%;
            scale: 1;
            background-color: antiquewhite;
            overflow: hidden;
        }

        /*设置lazy内部图片的style,根据自己需要调整,这里是填充显示中间部分*/
        /*悬浮时将会缩放图片*/
        .lazy > img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            /*property duration timing-function delay*/
            transition: all 0.2s ease-in 0s;
        }

        .lazy > img:hover {
            transform: scale(1.2, 1.2);
        }
        /*=============================*/
    </style>
</head>
<body>
<h1>H1</h1>
<div>H1</div>
<div>H1</div>
<div class="container">
    <div class="lazy" data-src="https://www.flywinter.com/assets/images/cover/2022-05-10-Veiled.png"></div>
</div>
<div class="container">
    <div class="lazy" data-src="https://www.flywinter.com/assets/images/cover/2022-05-10-Veiled.png"></div>
</div>
<div class="container">
    <div class="lazy" data-src="https://www.flywinter.com/assets/images/cover/2022-05-10-Veiled.png"></div>
</div>

<div class="container">
    <div class="lazy"
         data-src="https://www.flywinter.com/assets/images/cover/2022-05-10-%E9%9B%AA%E8%A7%A3%E3%81%91.png"></div>
</div>
<div class="container">
    <div class="lazy"
         data-src="https://www.flywinter.com/assets/images/cover/2022-05-10-%E9%9B%AA%E8%A7%A3%E3%81%91.png"></div>
</div>
<div class="container">
    <div class="lazy" 
         data-src="https://www.flywinter.com/assets/images/cover/2022-05-10-%E9%9B%AA%E8%A7%A3%E3%81%91.png"></div>
</div>

<div class="container">
    <div class="lazy" data-src="https://www.flywinter.com/assets/images/cover/2022-05-09-mikasa-sakura.png"></div>
</div>
<div class="container">
    <div class="lazy" data-src="https://www.flywinter.com/assets/images/cover/2022-05-09-mikasa-sakura.png"></div>
</div>
<div class="container">
    <div class="lazy" data-src="https://www.flywinter.com/assets/images/cover/2022-05-09-mikasa-sakura.png"></div>
</div>
<h2>H2</h2>
</body>
<script type="text/javascript">

    /**
     * 另一种实现思路,使用时,先新建被div包裹的img标签,
     * 这样就不需要重新创建元素,直接给lazy里面的img标签赋值即可
     * 但是缺点就是一定几率用户会看到加载失败的图片样式,影响体验
     * 可以通过背景图片,增大判断距离来解决
     *
     * 下面的实现思路是为每个添加了lazy的元素添加标签,
     * 只要处于可视范围,就新建img元素,并且为lazy元素设置属性,
     * 只要设置了属性为true,代表已经创建了元素,即已经加载,
     * 就不需要重新加载了
     */
    const lazyLoad = (imgContainers) => {
        //窗口高度
        const windowHeight = window.innerHeight;
        //滚动条滚动距离
        const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
        for (let i = 0; i < imgContainers.length; i++) {
            const imgContainer = imgContainers[i];
            const offsetTop = imgContainer.offsetTop;
            const loaded = imgContainer.getAttribute('data-loaded');
            //滚动+窗口的高度>图片元素到顶部的距离
            //多加256的高度,让图片接近的时候就开始加载
            //这时图片在窗口
            // <----windowHeight + scrollTop---->
            // <-----------offsetTop------------>imageContainer
            //如果图片显示了,并且没有加载过,就加载图片,防止重复创建img
            const isShow = (windowHeight + scrollTop +256) > offsetTop;
            if (isShow && loaded === 'false') {
                const img = document.createElement('img');
                const imgSrc = imgContainer.getAttribute('data-src');
                img.src = imgSrc;
                img.alt = imgSrc.split("/").pop()
                imgContainer.appendChild(img)
                imgContainer.setAttribute('data-loaded', 'true')
            }
        }
    };

    //HTML元素都加载完毕了再添加事件
    window.addEventListener('load', () => {
        const imgContainers = document.querySelectorAll('.lazy');
        for (let i = 0; i < imgContainers.length; i++) {
            imgContainers[i].setAttribute('data-loaded', 'false')
        }
        //有时即使元素都加载完毕了,网页还是空白,所以延迟
        //但是这里延迟可以去掉,只保留lazyLoad(),
        // 因为实际的要求往往是只要请求网页就马上加载可视的图片,加了延迟反而显得有些卡
        setTimeout(() => {
            lazyLoad(imgContainers)
        }, 50)
        //只要滚动,就检测是不是需要请求图片
        window.addEventListener('scroll', () => {
                lazyLoad(imgContainers);
            }
        )
    })
</script>
</html>

4. 使用方法

将js代码以及.lazy.lazy > img这两个class拷贝到项目中,原来需要用到img标签的地方,替换为<div class="lazy" data-src="imagePath">,这样就可以实现图片懒加载