• 【JavaScript】前端性能优化-图片懒加载(附源码)


    一、效果图如下

    上面的效果图,效果需求如下

            1、还没加载图片的时候,默认显示加载图片背景图

            2、刚开始进入页面,自动加载第一屏幕的图片

            3、下拉界面,当一张图片容器完全显露出屏幕,即刻加载图片,替换背景图

            4、加载图片的时候,有渐进显示图片效果

    二、难点

            1)如何Ajax请求数据

            2)如何动态将json数据绑定到html中。

            3)如何通过对图片的定位计算,触发图片懒加载机制

            4)加分项,显示图片时有渐现的过渡动画

    三、前期知识点

            1)Ajax相关知识,XMLHttpRequest对象,所有现代的浏览器都支持此对象。

            2)innerHTML,数据绑定使用字符串拼接的方式

            3)HTML DOM getAttribute() 方法,返回自定属性名的属性值(主要是用于返回自定义属性的属性值)

            4)图片的 onload事件,当图片的src属性的属性值为正确(即能成功加载图片),才能触发图片的onload事件

    四、难点逐一攻破

            1)如何Ajax请求数据

            分四步走

    // 1)首先创建一个Ajax对象
    var xhr = new XMLHttpRequest;
    // 2)打开我们需要请求的数据的那个文件地址
    // URL地址后面加随机数目的:清除每一次请求数据时候(get请求)产生的缓存
    // 因为每次访问的地址不一样,样浏览器就不会尝试缓存来自服务器的响应,读取本地缓存的数据。
    xhr.open('get', 'json/newsList.txt?' + Math.random(), false); // false代表同步
     // 3)监听请求的状态
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && /^2d{2}$/.test(xhr.status)) {
            var val = xhr.responseText;
            jsonData = utils.jsonParse(val);
        }
    }
    // 4)发送请求
    xhr.send(null);

            2)如何动态将json数据绑定到html中。

            字符串拼接的方式(数据绑定中最常用的方式),即通过使用innerHTML,对页面元素进行字符串拼接,再重新渲染到页面中

    var str = "";
    if (jsonData) {
        for (var i = 0, len = jsonData.length; i < len; i++) {
            var curData = jsonData[i];
            str += '<li>';
            str += '<div><img src="" trueImg="' + curData["img"] + '"></div>';
            str += '<div><h2>' + curData["title"] + '</h2>';
            str += '<p>' + curData["desc"] + '</p>';
            str += '</div>';
            str += '</li></div>';
        }
        news.innerHTML += str;
    }    

            优势:数据绑定最常用的方式,因为浏览器只需要渲染一次(所有模板引擎数据绑定的原理就是字符串拼接,vue、angular、jade、kTemplate.js等等)
                       事先把内容拼接好,最后统一添加到页面中,只引发一次回流

            弊端:我们把新凭借的字符串添加到#ul1中,原有的三个li的鼠标滑过效果都消失了(原来标签绑定的事件都消失了)
                       原来,oUl.innerHTML的作用是把原来的标签以字符串的方式取出,原来作为标签的时候,对应事件绑定的东西已经没有了,然后进行字符串拼接,
                       但是,拼接完成之后,还是字符串!最后再把字符串统一添加到页面中,浏览器还需要把字符串渲染成为对应的标签

            3)如何通过对图片的定位计算,触发图片懒加载机制(关键点

            思路:

                    A:代表图片距离屏幕顶部的距离 

    //这里使用了utils工具类中的offset方法,具体实现看下面源码
    var A = utils.offset(curImgPar).offsetTop + curImgPar.offsetHeight; 

                    B:代表一屏幕距离+滚动条滚动距离

    //这里使用了utils工具类中的win方法,具体实现看下面源码
    var B = utils.win("clientHeight") + utils.win("scrollTop");

                    A < B的时候,此时懒加载的默认图片才能完整显示出来,这个时候就需要触发图片懒加载

            4)加载图片的时候,有渐进显示图片效果

            思路,利用window.setInterval 方法,通过对当前图片的透明度属性(curImg.style.opacity) 从透明0开始到透明度1,变化总时间为500ms即可

    // ->实现渐现效果
    function fadeIn(curImg) {
        var duration = 500, // 总时间
        interval = 10, //10ms走一次
        target = 1; //总距离是1
        var step = (target / duration) * interval; //每一步的步长
        var timer = window.setInterval(function () {
            var curOp =  utils.getCss2SS(curImg, "opacity");
            if (curOp >= 1) {
                curImg.style.opacity = 1;
                window.clearInterval(timer);
                return
            }
            curOp += step;
            curImg.style.opacity = curOp;
        }, interval);
    }
    

     

    五、完整代码

          实现懒加载只需要下面3个文件即可,需要请自行拷贝

            1)main.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <!--做移动端响应式布局页面,都需要加下面的meta-->
        <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!--meta:vp+tap一键生成-->
        <title>多张图片的延迟加载</title>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
                font-family: "Microsoft Sans Serif";
                font-size: 14px;
            }
            ul, li {
                list-style: none;
            }
            img {
                display: block;
                border: none;
            }
            .news {
                padding: 10px;
            }
            .news li {
                position: relative;
                height: 60px;
                padding: 10px 0;
                border-bottom: 1px solid #eee;
            }
            .news li > div:first-child {   /*意思是,li下面的子div,中的第一个*/
                position: absolute;
                top: 10px;
                left: 0;
                 75px;
                height: 60px;
                background: url("./img/loading.PNG") no-repeat center center #e1e1e1;
                background-size: 100% 100%;
            }
            /*移动端布局,最外层容器是不设置宽高的*/
    
            .news li > div:first-child img {
                display: none;
                 100%;
                height: 100%;
                opacity: 0;  /*这里设置为0的目的是,实现渐进的效果,后面的fadeIn函数,作用就是让图片透明都从0变成1*/
            }
    
            .news li > div:nth-child(2) {
                height: 60px;
                margin-left: 80px;
            }
            .news li > div:nth-child(2) h2 {
                height: 20px;
                line-height: 20px;
                /*实现文字超出一行自动裁切*/
                overflow: hidden;
                text-overflow: ellipsis; /*超出部分省略号显示*/
                white-space: nowrap; /*强制不换行*/
            }
            .news li > div:nth-child(2) p {
                line-height: 20px;
                font-size: 12px;
                color: #616161;
            }
        </style>
    </head>
    <body>
        <ul id="news" class="news">
            <!--<li>-->
                <!--<div>-->
                    <!--<img src="./img/new1.PNG" alt="">-->
                <!--</div>-->
                <!--<div>-->
                    <!--<h2>香港四大家族往事,香港四大家族往事,香港四大家族往事</h2>-->
                    <!--<p>香港四大家族往事:李嘉诚为郑裕彤扶灵香港四大家族往事:李嘉诚为郑裕彤扶灵</p>-->
                <!--</div>-->
            <!--</li>-->
        </ul>
    
    
    
    
    <script type="text/javascript" src="./tool/utils.js"></script>
    <script type="text/javascript">
        var news = document.getElementById("news"),
            imgList = news.getElementsByTagName("img");
    
        // 1、获取需要绑定的数据(通过Ajax)
        var jsonData = null;
        ~function () {
            // 1)首先创建一个Ajax对象
            var xhr = new XMLHttpRequest;
            // 2)打开我们需要请求的数据的那个文件地址
            // URL地址后面加随机数目的:清除每一次请求数据时候(get请求)产生的缓存
            // 因为每次访问的地址不一样,样浏览器就不会尝试缓存来自服务器的响应,读取本地缓存的数据。
            xhr.open('get', 'json/newsList.txt?' + Math.random(), false); // false代表同步
            // 3)监听请求的状态
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && /^2d{2}$/.test(xhr.status)) {
                    var val = xhr.responseText;
                    jsonData = utils.jsonParse(val);
                }
            }
            // 4)发送请求
            xhr.send(null);
        }();
        console.log(jsonData);
    
        // 2、数据绑定(使用字符串拼接的方式)
        ~function () {
            var str = "";
            if (jsonData) {
                for (var i = 0, len = jsonData.length; i < len; i++) {
                    var curData = jsonData[i];
                    str += '<li>';
                    str += '<div><img src="" trueImg="' + curData["img"] + '"></div>';
                    str += '<div><h2>' + curData["title"] + '</h2>';
                    str += '<p>' + curData["desc"] + '</p>';
                    str += '</div>';
                    str += '</li></div>';
                }
                news.innerHTML += str;
            }
        }();
    
        // 3、图片延迟加载
        // ->首先实现单张图片的延时加载
        function lazyImg(curImg) {
            var oImg = new Image;
            oImg.src = curImg.getAttribute("trueImg");
            oImg.onload = function() {
                curImg.src = this.src;
                curImg.style.display = "block";
                fadeIn(curImg);
                oImg = null;
            }
            curImg.isLoad = true;
        }
    
        // -> 循环处理每一张图片
        function handleAllImg() {
            for (var i = 0, len = imgList.length; i < len; i++) {
                var curImg = imgList[i];
                if (curImg.isLoad) { // 当前图片处理过的话,就不需重新进行处理
                    continue;
                }
    
                // ->只有当A小于B的时候再进行处理
    //          var A = utils.offset(curImg).top + curImg.offsetHeight; // 这里A不能这么计算,因为此时图片是隐藏的,没有图片,他的offsetHeight当让也是为0
                                                                          // 如果我要的到图片的A值,我们可以通过拿到他父节点的容器就行了,哈哈
                var curImgPar = curImg.parentNode,
                    A = utils.offset(curImgPar).offsetTop + curImgPar.offsetHeight,
                    B = utils.win("clientHeight") + utils.win("scrollTop");
                if (A < B) {
                    lazyImg(curImg);
                }
            }
        }
    
        // ->实现渐现效果
        function fadeIn(curImg) {
            var duration = 500, // 总时间
                interval = 10, //10ms走一次
                target = 1; //总距离是1
            var step = (target / duration) * interval; //每一步的步长
            var timer = window.setInterval(function () {
                var curOp =  utils.getCss2SS(curImg, "opacity");
                if (curOp >= 1) {
                      curImg.style.opacity = 1;
                      window.clearInterval(timer);
                      return
                }
                curOp += step;
                curImg.style.opacity = curOp;
            }, interval);
        }
    
        // 4、开始的时候(过500ms)加载1屏幕的图片,当滚动条滚动的时候,加载其他图片
        window.setTimeout(handleAllImg, 500);
        window.onscroll = handleAllImg;
        
    </script>
    </body>
    </html>
    

      

            2)utils.js

    // 为了与全局变量冲突,我们使用单例模式
    var utils = {
      // jsonParse: 把JSON格式的字符串转化为JSON格式的对象
      jsonParse: function (str) {
          var val = null;
           try {
              val = JSON.parse(str);
          } catch (e) {
              val = eval('(' + str + ')');
          }
          return val;
      },
    
      getCss2SS : function(curEle, attr) {
          var val = null, reg = null;
          if ('getComputedStyle' in window) {
              val = window.getComputedStyle(curEle, null)[attr];
          } else {
              if (attr === 'opacity') {
                  val = curEle.currentStyle[attr]; // ->返回 alpha(opacity=10)
                  reg = /^alpha(opacity=(d+(?:.d+)?))$/i;  //  获取10这个数字
                  val = reg.test(val)?reg.exec(val)[1]/100:1  // 超厉害,test与exec一起使用!!!
              }
              val = curEle.currentStyle[attr];
          }
          reg = /^-?d+(.d+)?(px|pt|rem|em)?$/i; //匹配的情况:纯数值或者带单位的数值
          return reg.test(val) ? parseFloat(val) : val;
      },
    
      offset : function(curEle) {
          var totalLeft = null,
              totalTop = null,
              par = curEle.offsetParent;
          // 首先把自己本身的进行累加
          totalLeft += curEle.offsetLeft;
          totalTop += curEle.offsetTop;
    
          while (par) {
              if (navigator.userAgent.indexOf("MSIE 8.0") === -1) {
                  // 累加父级参照物边框
                  totalTop += par.clientTop;
                  totalLeft += par.clientLeft;
              }
              // 累加父级参照物本身的偏移
              totalTop += par.offsetTop;
              totalLeft += par.offsetLeft;
              par = par.offsetParent;
          }
          console.log('offsetTop: ' + totalTop + ', offsetLeft: ' + totalLeft);
          var result = {};
          result.offsetTop = totalTop;
          result.offsetLeft = totalLeft;
          return result;
      },
    
      win : function(attr, value) {
          if (value === undefined) {
              return document.documentElement[attr] || document.body[attr];
          }
          document.documentElement[attr] = value;
          document.body[attr] = value;
      }
    };
    

            3、json文件

    [{"img":"./img/new1.PNG", "title": "1网络强国战略与“十三五”十四大战略", "desc": "1互联网是二十世纪人类最大的发明,互联网是二十世纪人类最大的发明"},
     {"img":"./img/new2.PNG", "title": "2网络强国战略与“十三五”十四大战略", "desc": "2互联网是二十世纪人类最大的发明,互联网是二十世纪人类最大的发明"},
     {"img":"./img/new3.PNG", "title": "3网络强国战略与“十三五”十四大战略", "desc": "3互联网是二十世纪人类最大的发明,互联网是二十世纪人类最大的发明"}
    ]
    
  • 相关阅读:
    @bzoj
    @bzoj
    @codeforces
    @codeforces
    @bzoj
    @codeforces
    @codeforces
    @codeforces
    @NOIP2018
    反转字符串--C和Python
  • 原文地址:https://www.cnblogs.com/pengshengguang/p/11669176.html
Copyright © 2020-2023  润新知