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

一、 核心思想

    先将img标签中的src链接设为同一张图片,
    真正的图片地址存储在img标签的自定义属性中。
    当js监听到该图片元素进入可视窗口时,将自定义属性中的地址存储到src属性中。

二、具体实现

0.HTML部分

       <div class="wrapper" id="box"></div>

1.创建要懒加载的元素并设置默认加载图片

       // 先把这些资源的放在一个数组,需要的时候加载
       var imgarr = [
            'http://pic1.win4000.com/wallpaper/1/54977e4981369.jpg',
            'http://image2.xyzs.com/upload/78/e1/110709/20130831/137796023651298_0.jpg',
            'http://image1.xyzs.com/upload/c6/f3/111382/20130831/137796023398917_0.jpg',
            'http://image3.xyzs.com/upload/a2/b4/112369/20130831/137796022421499_0.jpg',
        ];
        var defaulUrl = 'https://5b0988e595225.cdn.sohucs.com/images/20191114/40514a41531044e985cee02793daf2bb.jpeg';

        // 获取容器
        var box = document.getElementById('box');

        // 创建要懒加载的元素并设置默认加载图片
        /*
          url: 图片的地址
          type: 要创建的元素类型
          defaulUrl: 默认图片
          target:盒子,将创建的元素插入其中 
        */
        var createElement = function (url, type, defaulUrl, target) {

            // 兼容处理

            // 必传参数
            if (!url) throw new Error("the first arguments is not give");
            if (!type) throw new Error("the second arguments is not give");

            //  非必传参数
            var target = target || document.body; //默认参数

            var calculator = {
                img: function () {
                    var img = new Image();
                    img.alt = '加载失败啦';
                    return img;
                }
            }

            // 多态,参数类型判断
            if (typeof url == 'string') {
                createDom(url);
            } else {
                // 若是数组
                url.forEach(function (item, index) {
                    createDom(item);
                });
            }

            function createDom(url) {
                var dom = calculator[type]();
                dom.src = defaulUrl;
                dom.dataset.src = url;
                target.appendChild(dom);
            }
        }

2.页面解析完加载元素

         window.onload = function () {

            // 创建元素
            createElement(imgarr, 'img', defaulUrl, box);

            // 页面加载完先执行一次
            lazyLoading();
        }

3.懒加载方法

(1)使用offsetTop - scrollTop < clientHeight;优化使用节流

       var lazyLoading = function () {

            // 获取元素
            var imgs = [].slice.call(document.querySelectorAll('img'));

            var clientHeight = document.documentElement.clientHeight; //固定的值 可视区高度
            var scrollTop = document.documentElement.scrollTop;//变化的值 滚动条滚动的距离

            imgs.forEach(function (img, i) {
                var offsetTop = img.offsetTop; //固定的值 相对于文档左上角

                if (offsetTop - scrollTop < clientHeight) {
                    img.src = img.dataset.src;
                }

            })
        }

(2)使用getBoundingClientRect();优化使用节流

       var lazyLoading = function () {

            // 获取元素
            var imgs = [].slice.call(document.querySelectorAll('img'));

            var clientHeight = document.documentElement.clientHeight;

            imgs.forEach(function (img, i) {
                // 判断该图片是否能够加载----也就是判断图片是否在可视区域viewport
                // 每个dom对象都有一个函数 getBoundingClientRect()
                // getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。 
                var rect = img.getBoundingClientRect();
                if (rect.bottom <= 0) {
                    return false;
                }
                if (rect.top >= clientHeight) {
                    return false;
                }

                //加载图片
                img.src = img.dataset.src;//自定义data-src属性的获取
            })

        }

(3)使用IntersectionObserver API ,不需要使用节流

        //参考阮一峰博客:http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
        var lazyLoading = function () {
            var imgObserver = new IntersectionObserver(function (entrys) {
                entrys.forEach(function (entry) {

                    // 可见元素entry.intersectionRatio > 0
                    if (entry.intersectionRatio > 0) {
                        let img = entry.target;
                        img.src = img.dataset.src;
                        imgObserver.unobserve(img);
                    }
                })
            });

            // 获取元素
            var imgs = [].slice.call(document.querySelectorAll('img'));
            imgs.forEach(function (img) {
                imgObserver.observe(img);
            })
        }

三、节流

1. 对方法(1)和方法(2)的优化

        // 触发执行 缺点:触发频繁太耗性能
        // document.addEventListener('scroll', lazyLoading);

        // 改进: 使用节流:将多次执行变成每隔一段时间执行。
        /**
            * 函数节流方法
            * @param Function fn 延时调用函数
            * @param Number delay 延迟多长时间
            * @param Number atleast 至少多长时间触发一次
            * @return Function 延迟执行的方法
         */
        var throttle = function (fn, delay, atleast) {
            var timer = null;  //闭包的体现 ,相当于是*相对的全局变量*哦。
            var previous = null;

            return function () {
                var now = +new Date();

                if (!previous) previous = now;
                // 逻辑流程走的是“至少触发一次”的逻辑
                if (atleast && now - previous > atleast) {
                    fn();
                    // 重置上一次开始时间为本次结束时间
                    previous = now;
                    clearTimeout(timer);
                } else {
                    //timeout 的延迟逻辑
                    clearTimeout(timer);
                    timer = setTimeout(function () {
                        fn();
                        previous = null;
                    }, delay);
                }
            }
        };
        window.onscroll = throttle(lazyLoading, 500, 1000);

2.补充节流和防抖的应用场景

参考本篇文章

四、封装jQuery插件

		//封装的插件
        $.fn.extend({
            imgLazyLoading: function () {
                // console.log('我是图片懒加载');
                // console.log(this); //jQuery对象

                var imgObserver = new IntersectionObserver(function (entrys) {
                    entrys.forEach(function (entry) {

                        // 可见元素entry.intersectionRatio > 0
                        if (entry.intersectionRatio > 0) {
                            let img = entry.target;
                            img.src = img.dataset.src;
                            imgObserver.unobserve(img);
                        }
                    })
                });

                // 获取元素
                var imgs = Array.from(this); //想办法把jQuery对象转为数组
                imgs.forEach(function (img) {
                    imgObserver.observe(img);
                });
            }
        })

		// 这样使用
        $(function () {
            var imgs = $('img');
            imgs.imgLazyLoading();
        })

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值