基于纯JS实现多张图片的懒加载Lazy过程解析


Posted in Javascript onOctober 14, 2019

一、效果图如下

基于纯JS实现多张图片的懒加载Lazy过程解析

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

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 && /^2\d{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;
} <strong> </strong>

优势:数据绑定最常用的方式,因为浏览器只需要渲染一次(所有模板引擎数据绑定的原理就是字符串拼接,vue、angular、jade、kTemplate.js等等)

事先把内容拼接好,最后统一添加到页面中,只引发一次回流

弊端:我们把新凭借的字符串添加到#ul1中,原有的三个li的鼠标滑过效果都消失了(原来标签绑定的事件都消失了)
原来,oUl.innerHTML的作用是把原来的标签以字符串的方式取出,原来作为标签的时候,对应事件绑定的东西已经没有了,然后进行字符串拼接,

但是,拼接完成之后,还是字符串!最后再把字符串统一添加到页面中,浏览器还需要把字符串渲染成为对应的标签

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

基于纯JS实现多张图片的懒加载Lazy过程解析

思路:

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);
}

五、完整代码

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;
      width: 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;
      width: 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 && /^2\d{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互联网是二十世纪人类最大的发明,互联网是二十世纪人类最大的发明"}
]

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
来自国外的14个图片放大编辑的jQuery插件整理
Oct 20 Javascript
使用apply方法处理数组的三个技巧[译]
Sep 20 Javascript
关于jquery中全局函数each使用介绍
Dec 10 Javascript
href下载文件根据id取url并下载
May 28 Javascript
jQuery基于ajax()使用serialize()提交form数据的方法
Dec 08 Javascript
简单谈谈gulp-changed插件
Feb 21 Javascript
JS实现websocket长轮询实时消息提示的效果
Oct 10 Javascript
微信小程序实现图片压缩功能
Jan 26 Javascript
通过js动态创建标签,并设置属性方法
Feb 24 Javascript
微信小程序实现炫酷的弹出式菜单特效
Jan 28 Javascript
微信接入之获取用户头像的方法步骤
Sep 23 Javascript
JavaScript 实现继承的几种方式
Feb 19 Javascript
VUE+node(express)实现前后端分离
Oct 13 #Javascript
javascript sort()对数组中的元素进行排序详解
Oct 13 #Javascript
javaScript把其它类型转换为Number类型
Oct 13 #Javascript
js 实现watch监听数据变化的代码
Oct 13 #Javascript
15 分钟掌握vue-next响应式原理
Oct 13 #Javascript
Vue3.x源码调试的实现方法
Oct 13 #Javascript
使用webpack将ES6转化ES5的实现方法
Oct 13 #Javascript
You might like
PHP新特性之字节码缓存和内置服务器
2017/08/11 PHP
PHP PDO和消息队列的个人理解与应用实例分析
2019/11/25 PHP
JavaScript 实现??打印?理
2007/04/28 Javascript
javascript 图片上传预览-兼容标准
2009/06/01 Javascript
jQuery 前的按键判断代码
2010/03/19 Javascript
Javascript在IE下设置innerHTML时出现未知的运行时错误的解决方法
2011/01/12 Javascript
14款NodeJS Web框架推荐
2014/07/11 NodeJs
极易被忽视的javascript面试题七问七答
2016/02/15 Javascript
探索angularjs+requirejs全面实现按需加载的套路
2016/02/26 Javascript
浅谈jquery的map()和each()方法
2016/06/12 Javascript
Mvc提交表单的四种方法全程详解
2016/08/10 Javascript
使用DeviceOne实现微信小程序功能
2016/12/29 Javascript
js实现导航栏中英文切换效果
2017/01/16 Javascript
获取IE浏览器Cookie信息的方法
2017/01/23 Javascript
原生js编写2048小游戏
2017/03/17 Javascript
Vue学习笔记进阶篇之vue-cli安装及介绍
2017/07/18 Javascript
Webpack 服务器端代码打包的示例代码
2017/09/19 Javascript
JS实现的集合去重,交集,并集,差集功能示例
2018/03/13 Javascript
jQuery滚动条美化插件nicescroll简单用法示例
2018/04/18 jQuery
vue跳转方式(打开新页面)及传参操作示例
2020/01/26 Javascript
解决Element中el-date-picker组件不回填的情况
2020/11/07 Javascript
[48:30]LGD vs infamous Supermajor小组赛D组 BO3 第一场 6.3
2018/06/04 DOTA
[40:05]LGD vs Winstrike 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
pandas apply 函数 实现多进程的示例讲解
2018/04/20 Python
scikit-learn线性回归,多元回归,多项式回归的实现
2019/08/29 Python
python selenium实现发送带附件的邮件代码实例
2019/12/10 Python
Python 创建TCP服务器的方法
2020/07/28 Python
英国潮流网站:END.(全球免邮)
2017/01/16 全球购物
Hotels.com加拿大:领先的在线住宿网站
2018/10/05 全球购物
工作违纪检讨书
2014/02/17 职场文书
学校门卫岗位职责
2014/03/16 职场文书
二年级语文下册复习计划
2015/01/19 职场文书
订货会邀请函
2015/01/31 职场文书
总经理助理岗位职责
2015/01/31 职场文书
新闻报道稿范文
2015/07/23 职场文书
Golang 语言控制并发 Goroutine的方法
2021/06/30 Golang