手搓图片懒加载-图片预加载

7 篇文章 0 订阅
3 篇文章 0 订阅

在研究图片懒加载前先要知道图片懒加载的实现原理。
  • 图片懒加载的实现原理就是判断元素是否在浏览器可视范围,如果在可视范围则设置img的src为真实地址。如果未在可视区域则显示默认的图片,如加载中等的字样。(你问我什么是可视范围?别问,问就是自己百度。)。当然这个判断元素是否在可视范围不仅仅能做图片懒加载,其它用途也挺过,比如无限滚动,虚拟列表等。
  • 那么知道原理后就好办了,现在就需要知道如何判断img是否在可视范围,这里我个人有三种方案,客观请往下看。
判断元素是否在可视范围(三种)
  • 方案一 (手动计算法)
    固定判断公式:

el.offsetTop - document.documentElement.scrollTop <= viewPortHeight

封装为函数,(小二,上代码)。
(来咯,客观你要的函数,请慢用)

function isInViewPortOfOne (el) {
      // 得到当前浏览器得可视区域高度,(不包含滚动条及隐藏区域)
      const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
      // 得到目标元素距离视图顶部得距离
      const offsetTop = el.offsetTop
      // 得到滚动条距离顶部得高度
      const scrollTop = document.documentElement.scrollTop
      // 用offsetTop-scrollTop如果小于或者等于视图的高度那么证明元素在可视区域
      const top = offsetTop - scrollTop
      return top <= viewPortHeight
    }
  • 方案二 Api (el.getBoundingClientRect())
function isInViewPort(element) {
      const viewWidth = window.innerWidth || document.documentElement.clientWidth;
      const viewHeight = window.innerHeight || document.documentElement.clientHeight;
      // top=得到目标元素距离视图顶部得距离
      const {
        top,
        right,
        bottom,
        left,
      } = element.getBoundingClientRect();

      return (
              top >= 0 &&
              left >= 0 &&
              right <= viewWidth &&
              bottom <= viewHeight
      );
    }
  • 方案三 观察者模式 IntersectionObserver 构造函数
    关于IntersectionObserver的相关文档请移步到 点我
    const options = {
      // 表示重叠面积占被观察者的比例,从 0 - 1 取值,
      // 1 表示完全被包含
      threshold: [0],
      // 类似css的margin  用于标记观观察元素元父容器的margin交会时触发。
        // 比如现在是  -100px 那么现在img就会在距离视图底部-100px时触发并切换src,简单理解类似margin-bottom:100
      rootMargin: '-150px' // 这个-150是为了更加直观的感受到懒加载,数字越大触发时间越早
    };
        const callback = function(entries, observer) {
      entries.forEach(entry => {
        entry.isIntersecting // 当前元素是否在可视区域,如果在可视区域则可以进行处理需要的逻辑
        entry.target;             // 被观察者
      });
    };
    const observer = new IntersectionObserver(callback, options);
    const target = document.querySelector('.spanel');
    // 开始监听元素
    observer.observe(target);
实现案例 (两种实现方式)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <style>
    .zwbox{

    }
    .spanel{
        height: 200px;
        width: 200px;
        margin: 10px;
        transition: all 1s;
    }
    .container{

    }
  </style>
</head>
<body>
  <div class="zwbox"></div>
  <div class="container"> </div>
  <script>
  	// 准备基础内容
  	const eltotal = document.createDocumentFragment()
    for (let i = 0; i < 40; i++) {
        let el = document.createElement('img')
        el.src='https://tva3.sinaimg.cn/large/006APoFYly1g8mf9qe5udg30jz0jzjtr.gif'
        el.className='spanel'
        eltotal.append(el)
    }
    document.querySelector('.container').append(eltotal)
    const elArr = document.querySelectorAll('.spanel')
	
	/**
	* 方案一 用事件监听的方式(缺点,事件高频触发导致性能不好)
	* 这种方式在首屏时因为没有滚动触发不了事件,首屏的图片也不会加载,解决办法就是开局手动调用一次
	*/ 
	/**
	 window.addEventListener('scroll',()=>{
   	 elArr.forEach(el=> {
            // if(isInViewPortOfOne(el)){
            if(isInViewPort(el)){
               el.src='https://img2.baidu.com/it/u=861863691,2776527252&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500'
            }
        })
    })
    */
	// 方法二 观察者模式
	function getYellow(entries, observer) {
      entries.forEach(entry => {
          // 判断元素是否在可视区域,
          if(entry.isIntersecting){
              // setTimeout(()=>{
                  entry.target.src='https://img2.baidu.com/it/u=861863691,2776527252&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500'
              // },500)
          //    如果只触发一次的话在触发完成后就取消当前的el监听,提高性能
              observer.unobserve(entry.target)
          }
      });
  }
  const observer = new IntersectionObserver(getYellow, options);
    elArr.forEach(el=> observer.observe(el))
  </script>
</body>
</html>

图片预加载

/**
* 图片预加载
* 先说使用场景吧。当一个站点中某些页面的图片比较大时,会出现图片短暂的不显示,或者一部分一部分的显示出来。体验不是很好
* 那么此时就可以用预加载来处理
* 还是先上代码在讲道理 用上面图片懒加载来做案例
* */

function getYellow(entries, observer) {
        entries.forEach(entry => {
            if(entry.isIntersecting){
                // 假设现在这张图片很大
                let src = 'https://img2.baidu.com/it/u=861863691,2776527252&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500'
                const image = new Image()
                image.src = src
                // 监听图片加载完成
                image.onload = function (){
                    // 因为http get请求浏览器有缓存机制,当前这个地址的图片已经加载过,下一次使用就会直接取缓存
                    entry.target.src=src
                    observer.unobserve(entry.target)
                }
            }
        });
    }
    const observer = new IntersectionObserver(getYellow, options);
    elArr.forEach(el=> observer.observe(el))
  • 如果哪里说的不对欢迎来喷我,老铁点赞不迷路
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值