图片延迟加载

前言

图片延迟加载:真实项目中非常重要的一个性能优化手段;

  • 在没有加载图片之前,图片区域用默认的背景图(背景色)占位即可
  • 首屏:先加载首屏的其他数据(内容,样式,文本等),等待其它数据加载完成,再去加载真实的图片
  • 其他屏:当我们滚动到指定区域(一露面,出来一半,完全出现在屏幕中),在去加载真实图片

   onscroll特点:

只要页面滚动就会触发 < 在浏览器最快反应时间内(谷歌4 - 6ms), 触发一次, 触发频率特别频繁 >, 重复加载a <= b的这个条件, 只要页面一直向上滚动, 在第一次符合条件后, 每次滚动条件也是符合的, 这样 lazyImage方法会被执行多次(也就是延迟加载会被处理多次)

处理频率太快,我们需要降低他的频率 函数的节流throttle

意义: 如果不做延迟加载,那么页面渲染的时候,同时也要把资源请求回来,并且进行渲染,这样会阻碍页面的渲染进度,导致首次加载页面变慢,延迟加载一方面,可以提高页面加载速度,另外一方面也可以减少用户没必要的网路消耗

单张图片延迟加载 方案一

需要延迟加载的图片放置在一个盒子中,

盒子有一个默认占位的背景图,在没有加载真实图片之前,我们给用户呈现的是一个默认的占位图

img一开始不能设置src,因为器一但设置src页面加载的时候,就会加载图片了,达不到延迟加载的效果

把真实加载的图片地址放到image的lazy-image自定义属性上(自定义属性名自己设定即可)

注意: 开始图片是隐藏的:在某些IE浏览器中,如果图片的SRC是空的,或者加载的图片是错误的,图片不隐藏,会显示一个 X 的效果,很难看,所以图片没有加载真实地址,并且加载正确之前,还是让图片隐藏吧!

  • 办法一:display: none;  ->这种办法在加载完成真实图片后,还需要让其display: block;这样触发了DOM的回流重汇,性能消耗比较大...
  •  办法二:opacity: 0; transition: opacity .3s;  ->推荐这种方案,一方面后期加载真实图片后,我们只需要设置opacity: 1;,这样一方面不会引发DOM的回流重绘,一方面还可以CSS3过度动画实现出渐现的效果...
//节流函数
        function throttle(func, wait = 500) {
            let timer = null,
                previous = 0;
            return function anonymous(...params) {
                let now = new Date(),
                    remaining = wait - (now - previous);
                if (remaining <= 0) {
                    clearTimeout(timer);
                    timer = null;
                    previous = now;
                    func.call(this, ...params);
                } else if (!timer) {
                    timer = setTimeout(() => {
                        clearTimeout(timer);
                        timer = null;
                        previous = new Date();
                        func.call(this, ...params);
                    }, remaining);
                }
            };
        }

        //盒子顶部距离body上偏移
        function offset(element) {
            let l = element.offsetLeft,
                t = element.offsetTop,
                p = element.offsetParent;
            while (p && p.tagName !== 'body') {
                if (!/MSIE 8/.test(navigator.userAgent)) {
                    l += p.clientLeft;
                    t += p.clientTop;
                }
                l += p.offsetLeft;
                t += p.offsetTop;
                p = p.offsetParent;
            }
            return {
                top: t,
                left: l
            };
        }

        //获取占位盒子 && html
        let imageLazyBox = document.querySelector('.imageLazyBox'),
            html = document.documentElement;

        //执行lazyImage方法,传递需要延迟加载的盒子,实现单张图片延迟加载
        function lazyImage(imageLazyBox) {
            // 获取图片
            let imageItem = imageLazyBox.querySelector('img'),
                // 获取图片的自定义属性
                lazy_image = imageItem.getAttribute('lazy-image');
            // if (lazy_image === null) return;//性能不好
            imageItem.onload = function () {
                //当图片加载成功后触发的触发 onload vs onerror
                imageItem.style.opacity = 1;
            };
            //图片的地址赋值给image的src
            imageItem.src = lazy_image;
            // 也有在不论图片是否加载成功,只要处理过,则就把lazy-image属性移除掉(证明其已经处理过了)
            imageItem.removeAttribute('lazy-image');
            //第一次执行这个方法,我们设置一个自定义属性isload = true 证明当前图片已经看过了
            imageLazyBox.isload = true;
        }
       
        //监听滚动条图片全部显示后加载图片
        window.onscroll = throttle(function () {
            console.log('ok');
            if (imageLazyBox.isload) return;
            // 盒子底部距离body的距离:盒子顶部的距离+盒子本身高度
            let a = offset(imageLazyBox).top + imageLazyBox.offsetHeight,
                // 浏览器底边距离body: 一屏的高度-滚动条卷去的高度
                b = html.clientHeight + html.scrollTop;
            if (a <= b) {
                lazyImage(imageLazyBox)
            }
        })


        //设定定时器,延迟多久之后进行加载
        // setTimeout(lazyImage, 1000);

        // 或者触发条件:页面中所有资源都加载完成
        // window.onload = function () { }; 

 思路:  //在浏览器滚动条滚动(页面滚动)过程中,等待图片完全出现在可视窗口内的时候,我们加载真实图片 "这是非首屏图片的处理步骤"-->计算出盒子底边距离body(a)的距离 和 浏览器底边距离body(b)的距离 做比较如果a<=b的时候加载真实图片即可

方案二

思路:基于getBoundingClientRect来判断图片的加载条件,不用自己写offset方法了

  • a:获取当前盒子距离浏览器可视窗口的位置信息
  • b可视窗口一屏的的大小
  • 做比较如果a<=b的时候加载真实图片即可
        function throttle(func, wait = 500) {
            let timer = null,
                previous = 0;
            return function anonymous(...params) {
                let now = new Date(),
                    remaining = wait - (now - previous);
                if (remaining <= 0) {
                    clearTimeout(timer);
                    timer = null;
                    previous = now;
                    func.call(this, ...params);
                } else if (!timer) {
                    timer = setTimeout(() => {
                        clearTimeout(timer);
                        timer = null;
                        previous = new Date();
                        func.call(this, ...params);
                    }, remaining);
                }
            };
        }

        let imageLazyBox = document.querySelector('.imageLazyBox'),
            html = document.documentElement;

        function lazyImage(imageLazyBox) {
            let imageItem = imageLazyBox.querySelector('img'),
                lazy_image = imageItem.getAttribute('lazy-image');
            imageItem.onload = function () {
                imageItem.style.opacity = 1;
            };
            imageItem.src = lazy_image;
            imageItem.removeAttribute('lazy-image');
            imageLazyBox.isload = true;
        }

        window.onscroll = throttle(function () {
            //获取当前盒子距离浏览器可视窗口的位置信息
            let a = imageLazyBox.getBoundingClientRect().bottom,
            // 可视窗口一屏的的大小
                b = html.clientHeight;
            if (imageLazyBox.isload) return;
            if (a <= b) {
                lazyImage(imageLazyBox)
            }
        })

方案三

基于 IntersectionObserver实现图片延迟加载

设置一个监听器,后期可以监听指定的dom元素(可以多个),当监听的元素dom元素与可视窗口交叉位置发生改变,触发回调函数执行,-->changes是一个集合,集合中存储了一个dom元素与可视窗口交叉的状态信息

 Observer.observe(imageLazyBox)监听dom元素

Observer.unobserve(imageLazyBox):解除监听

默认:第一次监听dom触发一次页面刚开始出现在页面中触发一次,元素消失在页面中触发一次

         基于threshold配置项,控制触发监听条件,(0刚出现, 0.5出现一半, 1完全出现)

  •  IntersectionObserverEntry 每一个dom元素与可视窗口交叉的信息
  • boundingClientRect获取就是基于imageLazyBox.getBoundingClientRect()获取的结果
  • target:监听的dom元素
  • isIntersecting:是否与可视窗口交叉了(布尔),如果为true说明元素出现在可视窗口中,反之如果为false说明不在可视窗口中
 let imageLazyBox = document.querySelector('.imageLazyBox'),
            //配置项只有完全触发出现
            options = {
                threshold: [1]
            };
        function lazyImage(imageLazyBox) {
            let imageItem = imageLazyBox.querySelector('img'),
                lazy_image = imageItem.getAttribute('lazy-image');
            imageItem.onload = function () {
                imageItem.style.opacity = 1;
            };
            imageItem.src = lazy_image;
            imageItem.removeAttribute('lazy-image');
            imageLazyBox.isload = true;
        }
        let ob = new IntersectionObserver(change => {
            // item监听的dom元素与可视窗口的交叉信息
            // target存储当前监听的那个盒子
            let item = change[0],
                target = item.target;
            if (item.isIntersecting) {
                //符合条件:盒子已经完全出现在视口中
                lazyImage(target);
                // 处理过一次延迟加载,以后当前这个元素无需在次监听处理了
                ob.unobserve(imageLazyBox)
            }
        }, options)
        //监听dom元素
        ob.observe(imageLazyBox)

基于IntersectionObserver,实现延迟加载,相对于之前 window.onscroll实现延迟加载的优势

  • 优势:更方便 ,onscrol方法中需要自己编写throttle函数进行节流处理,以此优化性能;但是IntersectionObserver内置节流处理,而且性能上比onscroll也好很多!!
  •  弊端:兼容性不好,IE中只有EDGE新版本兼容,其余都不兼容,火狐谷歌等也得是 >=60 左右的新版本才兼容,所以移动端开发目前使用的比较多

Can I use... Support tables for HTML5, CSS3, etc测试方法兼容性的

方案四

 loading="lazy" 浏览器会自己计算该何时加载,帮助我们加载真实的图片

     <img src="images/1.jpg" loading="lazy">

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值