图片懒加载的三种方式

方法一:滚动监听 + scrollTop + offsetTop + innerHeight

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

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

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

html 复制代码
<!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>
        img {
            background: url('./img/loading.gif') no-repeat center;
            width: 250px;
            height: 250px;
            display: block;
        }
    </style>
</head>

<body>
    <img src="./img/pixel.gif" data-url="./img/1.jpeg">
    <img src="./img/pixel.gif" data-url="./img/2.jfif">
    <img src="./img/pixel.gif" data-url="./img/3.jfif">
    <img src="./img/pixel.gif" data-url="./img/4.jfif">
    <img src="./img/pixel.gif" data-url="./img/5.jfif">
    <img src="./img/pixel.gif" data-url="./img/6.webp">

    <script>

        let imgs = document.getElementsByTagName('img')
        // 1. 一上来立即执行一次
        fn()
        // 2. 监听滚动事件
        window.onscroll = lazyload(fn, true)
        function fn() {
            // 获取视口高度和内容的偏移量
            let clietH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
            var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
            console.log(clietH, scrollTop);
            for (let i = 0; i < imgs.length; i++) {
                let x = scrollTop + clietH - imgs[i].offsetTop //当内容的偏移量+视口高度>图片距离内容顶部的偏移量时,说明图片在视口内
                if (x > 0) {
                    imgs[i].src = imgs[i].getAttribute('data-url'); //从dataurl中取出真实的图片地址赋值给url
                }
            }
        }
          // 函数节流
        function lazyload(fn, immediate) {
            let timer = null
            return function () {
                let context = this;
                if (!timer) {
                    timer = setTimeout(() => {
                        fn.apply(this)
                        timer = null
                    }, 200)
                }
            }
        }


    </script>
</body>

</html>

方法二:滚动监听 + getBoundingClientRect()

rectObject.top:元素上边到视窗上边的距离;

rectObject.right:元素右边到视窗左边的距离;

rectObject.bottom:元素下边到视窗上边的距离;

rectObject.left:元素左边到视窗左边的距离;

rectObject.width:元素自身的宽度;

rectObject.height:元素自身的高度;

html 复制代码
<!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 {
            background: url('./img/loading.gif') no-repeat center;
            width: 250px;
            height: 250px;
            display: block;
        }
    </style>
</head>

<body>
    <img src="./img/pixel.gif" data-url="./img/1.jpeg">
    <img src="./img/pixel.gif" data-url="./img/2.jfif">
    <img src="./img/pixel.gif" data-url="./img/3.jfif">
    <img src="./img/pixel.gif" data-url="./img/4.jfif">
    <img src="./img/pixel.gif" data-url="./img/5.jfif">
    <img src="./img/pixel.gif" data-url="./img/6.webp">

    <script>

        let imgs = document.getElementsByTagName('img')
        // 1. 一上来立即执行一次
        fn()
        // 2. 监听滚动事件
        window.onscroll = lazyload(fn, true)
        function fn() {
            // 获取视口高度和内容的偏移量
            let offsetHeight = window.innerHeight || document.documentElement.clientHeight
            Array.from(imgs).forEach((item, index) => {
                let oBounding = item.getBoundingClientRect() //返回一个矩形对象,包含上下左右的偏移值
                console.log(index, oBounding.top, offsetHeight);
                if (0 <= oBounding.top && oBounding.top <= offsetHeight) {
                    item.setAttribute('src', item.getAttribute('data-url'))
                }
            })
        }
        // 函数节流
        function lazyload(fn, immediate) {
            let timer = null
            return function () {
                let context = this;
                if (!timer) {
                    timer = setTimeout(() => {
                        fn.apply(this)
                        timer = null
                    }, 200)
                }
            }
        }


    </script>
</body>

</html>

方法三: intersectionObserve()

IntersectionObserver : 浏览器原生提供的构造函数,接收两个参数

IntersectionObserver API 是异步的,不会与目标元素的滚动同步触发

html 复制代码
<!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>
        img {
            background: url('./img/loading.gif') no-repeat center;
            width: 250px;
            height: 250px;
            display: block;
        }
    </style>
</head>

<body>
    <img src="./img/pixel.gif" data-url="./img/1.jpeg">
    <img src="./img/pixel.gif" data-url="./img/2.jfif">
    <img src="./img/pixel.gif" data-url="./img/3.jfif">
    <img src="./img/pixel.gif" data-url="./img/4.jfif">
    <img src="./img/pixel.gif" data-url="./img/5.jfif">
    <img src="./img/pixel.gif" data-url="./img/6.webp">

    <script>

        let imgs = document.getElementsByTagName('img')
        // 1. 一上来立即执行一次
        let io = new IntersectionObserver(function (entires) {
            //图片进入视口时就执行回调
            entires.forEach(item => {
                // 获取目标元素
                let oImg = item.target
                // console.log(item);
                // 当图片进入视口的时候,就赋值图片的真实地址
                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>
相关推荐
盛夏绽放7 小时前
jQuery 知识点复习总览
前端·javascript·jquery
胡gh9 小时前
依旧性能优化,如何在浅比较上做文章,memo 满天飞,谁在裸奔?
前端·react.js·面试
大怪v9 小时前
超赞👍!优秀前端佬的电子布洛芬技术网站!
前端·javascript·vue.js
胡gh9 小时前
你一般用哪些状态管理库?别担心,Zustand和Redux就能说个10分钟
前端·面试·node.js
roamingcode11 小时前
Claude Code NPM 包发布命令
前端·npm·node.js·claude·自定义指令·claude code
码哥DFS11 小时前
NPM模块化总结
前端·javascript
灵感__idea11 小时前
JavaScript高级程序设计(第5版):代码整洁之道
前端·javascript·程序员
唐璜Taro11 小时前
electron进程间通信-IPC通信注册机制
前端·javascript·electron
陪我一起学编程13 小时前
创建Vue项目的不同方式及项目规范化配置
前端·javascript·vue.js·git·elementui·axios·企业规范
LinXunFeng13 小时前
Flutter - 详情页初始锚点与优化
前端·flutter·开源