• 图片懒加载的原理


    1,懒加载的概念

    对于页面有很多静态资源的情况下(比如网商购物页面),为了节省用户流量和提高页面性能,可以在用户浏览到当前资源(当前窗口(可视区域)的大小)的时候,再对资源进行请求和加载。

    2,懒加载实现的原理

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

    代码示例:

    <img
      src=""
      lazyload="true"
      data-src="https://t7.baidu.com/itu=1732966997,2981886582&fm=193&f=GIF"
      alt=""
      class="image-item"
    />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • data-src是我们自己定义的属性字段,它的值为我们要的图片地址
    • lazyload="true"是为了当图片加载出来后将图片不在需要懒加载了。

    3,可视区域表示

    原理:

    使用:document.documentElement.clientHeight 可以获取到当前屏幕的高度。

    过程:

    当页面发生上滑的时候,可视区域的图片就会发生改变。

    当鼠标滚轮滚动的时候,在可视区域的图片就可能出去可视区域了,不在的可能这时候就进来了可视区域。所以我们要写一个监听事件来进行监听

    当我们拿到所有的 img 时,利用循环去判断他们是否在可视区域内,在就加载出来,不在就暂时不加载。

    需要满足的条件是图片的顶部在可视区域的高度里面,图片的底部也要在可视区域里面,也就是图片没有被划出去。

    代码:

    
    //获取可视区域的高度
    var viewHeight = document.documentElement.clientHeight;
    document.addEventListener("scroll", function() {
      //获取到页面上所有的img
      //判断某个是否进入可视区域
      //如果进入,就把它自身的data-original的值取出来放到src
      var arr = document.querySelectorAll("img[data-src][lazyload]");
      arr.forEach((item) => {
        let rect = item.getBoundingClientRect(); //用于一次性获取某个容器相对于浏览器上下左右的位置
        if (rect.top < viewHeight && rect.bottom >= 0) {
          item.src = item.dataset.src;
          item.removeAttribute("data-src");
          item.removeAttribute("lazyload");
        }
      });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    获取区域的方法:

    • offsetTop方法: 图片出现在视窗内的情况: offsetTop < clientHeight + scrollTop
    • getBoundingClientRect方法: 图片出现在视窗内的情况: element.getBoundingClientRect().top < clientHeight
    • H5的intersectionObserve方法: intersectionRatio:目标元素的可见比例,即 intersectionRect 占 boundingClientRect 的比例,完全可见时为 1 ,完全不可见时小于等于 0

    4,代码实现方式

    (1) 使用 getBoundingClientRect方法

    // offsetTop
    // el.offsetTop - document.documentElement.scrollTop <= viewPortHeight
    function isInViewPortOfOne (el) {
        // viewPortHeight 兼容所有浏览器写法
        const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 
        const offsetTop = el.offsetTop
        const scrollTop = document.documentElement.scrollTop
        const top = offsetTop - scrollTop
        return top <= viewPortHeight
    }
    
    document.addEventListener('DomContentLoader', function(){
    	let lazyImages = [].slice.call(document.querySelectorAll('#imag.lazy'))
    	//限制函数频繁被调用
    	let active= false
    	const lazyLoad = function(){
    	if(active === false){
    		active = true
    		setTimout(()=>{
    			lazyImages.forEach((lazyImage)=>{
    				// 判断该图片是否在是视窗中
    				if((lazyImage.getBoundingClientReat().top <= window.innetHeight && lazyImage.getBoundingClientReat().bottom >= 0 ) && getComputedStyle(lazyImage).display !== 'none'){
    				// 将真实的图片进行赋值
    				lazyImage.src = lazyImage.dataset.src
    				// 加载完成之后,要为该图片移除lazy这个属性
    				lazyImage.classList.remove('lazy')
    				// 在lazyImage中将加载后的图片移除
    				lazyImages = lazyImages.filter((image)=>{
    					return image !== lazyImage
    				})
    				// 如果所有的待加载图片全都加载完成之后, 移除触发函数
    				if(lazyImage.length == 0){
    					document.removeEventListener('scroll', lazyLoad);
    					wimdow.removeEventListener('resize', lazyLoad)
    				}
    				}
    			});
    			active = false
    },200)
    }
    }
    	document.addEventListener('scroll', lazyLoad)
    	window.addEventListener('resize', lazyLoad)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    由于用户可以以随心所欲的滑动鼠标滚轮,从而导致scroll事件被触发。在此代码中,将延迟加载的处理过程置于一个异步定时器中,通过修改标志位active的方式来进行限流,其实就是加上了节流操作。
    即便这样,也是有骑在的性能问题,因为重复的定时器调用的浪费的。

    (2)使用Intersection Observer 方式

    对于Intersection Observer 的API,可以通过它来检查目标元素的可见性。作用是:每当因页面滚动或者窗口尺寸发生大小的时候,使得目标元素与设备视窗或者其他元素产生交集的时候,就会触发通过Intersection Observer API配置的回调函数,在该回调函数中进行延迟加载的逻辑处理,会比传统的方式更加简洁而高效。

    document.addEvemtListener('DOMContentLoaded', function(){
    	let lazyImages = [].slice.call(document.querySelectorAll('img.lazy'))
    	// 判断浏览器的兼容性
    	if('IntersectionObserver' in window && 'intersectionObserverEnter' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype){
    	//新建IntersectionObserver对象 并在其回调函数中实现加载逻辑
    	let lazyImageObserver = new IntersectionObserver(funtion(entries, observer){
    		entries.foreach((entry)=>{
    		// 判断图片是否在视窗内
    		if(entry.isIntersecting){
    		let lazyImage = entry.target
    		lazyImage.src = lazyImage.dataset.src
    		// 图片加载完成之后,取消监控重复加载
    		lazyImage.classList.remove('lazy')
    		lazyImageObserver.unobserver(lazyImage)
    }
    		})
    	})
    	// 对每一个懒加载的图片进行监听
    	lazyImages.forEach(function(lazyImage){
    	lazyImageObservr.observer(lazyImage);
    })
    }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    唯一存在的问题,就是兼容性问题,在使用的时候,可以和传统的图片懒加载方式结合起来。

  • 相关阅读:
    tomcat架构概览
    java——注释与空行
    阿里云使用记录
    页面置换算法(OPT、FIFO、LRU、时钟、LFU)
    谷粒商城1.项目简介和项目环境预搭建(项目概述和环境搭建代码)
    Qt QWidget 简约美观的加载动画 第五季 - 小方块风格
    srpingMVC基本使用
    Educational Codeforces Round 133 (Rated for Div. 2)
    聊一聊我对Restful理解
    Mysql 数据恢复逻辑 基于binlog redolog undolog
  • 原文地址:https://blog.csdn.net/wlijun_0808/article/details/128091644