JavaScript | 通过原生js教你学会图片懒加载

211 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

前言

接前几天讲解了DOM、BOM的基础知识后,用原生js实现图片懒加载。

图片懒加载

图片懒加载是前端页面优化的一种方式,在页面中有很多图片的时候,图片加载就需要很多时间,很耗费服务器性能,不仅影响渲染速度还会浪费带宽,为了解决这个问题,提高用户体验,所以就出现了懒加载这种方式来减轻服务器的压力,优先加载可视区域的内容,其他部分等进入了可视区域再加载,从而提高性能。

图片懒加载如何实现

  • 简单来说就是当js监听到图片元素进入到可视窗口的时候,将自定义属性中的地址存储到src中,达到懒加载的效果
  • 先将img标签中的src链接设置为空,将真正的图片链接放在自定义属性(data-src)

QQ20220607-0.png

具体实现思路

如上图,y代表整个页面的高度x代表滚动条滚动的距离z代表当前可视化窗口w代表未加载的img图片,而我们需要操作的就是当前所展示的图片的offsetTop(元素到顶部的距离) 小于等于 窗口的高度+滚动条滚动的高度,我们就展示图片。

  • 伪代码:offsetTop+clientLeft < =(document.documentElement.clientHeight)+(window.pageYOffset)

懒加载实现效果图

动画.gif

<!DOCTYPE html>
<html lang="en">
<body>
  <div class="outer"></div>
</body>

<script>
    var oOuter = document.querySelector('.outer');
    var imgSrc =[
'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a93ce8141c284e5f8f9fb9ed860e0115~tplv-k3u1fbpfcp-watermark.image?',
'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2935181e76cf4ad080544771d1dc309f~tplv-k3u1fbpfcp-watermark.image?',  
'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/713934abf3d44ebfbe4ac977c77cd85c~tplv-k3u1fbpfcp-watermark.image?',          
'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/18a58263915a4a91becda50af6aba75a~tplv-k3u1fbpfcp-watermark.image?',
'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/63e1ce8d64474c18bcb09313683a704b~tplv-k3u1fbpfcp-watermark.image?',
'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7d0020d9bbc24d66a2f659b5a29aaee2~tplv-k3u1fbpfcp-watermark.image?',
'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bfb1c1f1496d4900a7be1a9011c19b68~tplv-k3u1fbpfcp-watermark.image?',
'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7533eb9af02c4c61a9836ca09d2f6583~tplv-k3u1fbpfcp-watermark.image?',
'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aeceee670f1b42b8869d302e4b52466a~tplv-k3u1fbpfcp-watermark.image?',
'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/23fff46aff734773b5c73cd24182d7fb~tplv-k3u1fbpfcp-watermark.image?',
'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bfb1c1f1496d4900a7be1a9011c19b68~tplv-k3u1fbpfcp-watermark.image?',
'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7533eb9af02c4c61a9836ca09d2f6583~tplv-k3u1fbpfcp-watermark.image?',
'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aeceee670f1b42b8869d302e4b52466a~tplv-k3u1fbpfcp-watermark.image?',
'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/23fff46aff734773b5c73cd24182d7fb~tplv-k3u1fbpfcp-watermark.image?',
    ];

    imgSrc.forEach(function(item){
      var newImg = new Image
     //先把src保存在当前img的自定义属性上,等加载到的时候,在把自定义属性给到src属性
      newImg.src = 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4d4c7b477430450189b3cf4b9e887252~tplv-k3u1fbpfcp-watermark.image'
       newImg.dataset.src = item;
       oOuter.appendChild(newImg)
    })

    var oImgs = document.querySelectorAll('.outer img');

      function lazyLoad() {
          //获取窗口的高度
          var winHeight = document.documentElement.clientHeight;
          //获取滚动条已经滚动的距离
          var winScroll = window.pageYOffset;
          //遍历所有的图片
          oImgs.forEach(function (item) {
              //判断哪一个图片已经进入页面(图片距离文档顶部的值 小于 屏幕的高度加上滚动条滚过的距离)
              if (getEleToDoc(item).top <= winHeight + winScroll) {
                  //让图片显示  把当前img标签上保存的data-src 赋值给img的src
                  item.src = item.dataset.src;
              }
      })
  }

  //监听滚动条
  window.onload = window.onscroll = throttle(lazyLoad, 200);

  //获取元素到文档顶部或左侧边缘的距离封装
  function getEleToDoc(Ele) {
      var l = 0,
          t = 0;
      var obj = Ele;
      while (obj) {
          if (obj === Ele) {
              l += obj.offsetLeft;
              t += obj.offsetTop;
          } else {
              l += (obj.offsetLeft + obj.clientLeft);
              t += (obj.offsetTop + obj.clientTop);
          }

          //如果obj存在,并且已经获取过值了,则把obj变成 原来obj的offsetParent
          obj = obj.offsetParent;
      }

      return {
          left: l,
          top: t
      }
  }

    //节流函数
  function throttle(fn, time) {
      //绑定事件的时候,先初始化一个上一次的事件
      var lastTime = 0;
      //这个函数是事件触发的时候真正调用的事件函数
      return function () {
          //当允许通过的时候 再调用真正的逻辑代码move
          var nowTime = Date.now();
          if (nowTime - lastTime < time) {
              return;
          }
          lastTime = nowTime;

          //arguments所在的函数就是真正的事件函数,所以拥有实参event  把event事件对象传递给fn move中就可以使用event事件对象了
          // fn(arguments[0]);
          //改变了fn的this为事件触发的对象
          fn.call(this, arguments[0])
      }
  }

</script>

<style>
   .outer {
      width: 1040px;
      margin: 0 auto;
      border: 1px solid #000;
      display: flex;
      flex-wrap: wrap;

  }

  .outer img {
      width: 500px;
      height: 300px;
      margin: 10px;
  }
</style>
</html>

好了,以上就是本篇文章的分享,感谢阅读!