基于纯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 相关文章推荐
通用于ie和firefox的函数 GetCurrentStyle (obj, prop)
Dec 27 Javascript
从JavaScript 到 JQuery (1)学习小结
Feb 12 Javascript
javascript使用定时函数实现跳转到某个页面
Dec 25 Javascript
javascript制作的网页侧边弹出框思路及实现代码
May 21 Javascript
JavaScript实现动态删除列表框值的方法
Aug 12 Javascript
javascript同步服务器时间和同步倒计时小技巧
Sep 24 Javascript
使用jQuery调用XML实现无刷新即时聊天
Aug 07 Javascript
基于Vuex无法观察到值变化的解决方法
Mar 01 Javascript
Angular 组件之间的交互的示例代码
Mar 24 Javascript
Easyui 关闭jquery-easui tab标签页前触发事件的解决方法
Apr 28 jQuery
JavaScript使用百度ECharts插件绘制饼图操作示例
Nov 26 Javascript
Vue3 响应式侦听与计算的实现
Nov 11 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中操作Excel实例代码
2010/04/29 PHP
6个超实用的PHP代码片段
2015/08/10 PHP
PHP实现清除MySQL死连接的方法
2016/07/23 PHP
thinkphp中U方法按路由规则生成url的方法
2018/03/12 PHP
Thinkphp自定义生成缩略图尺寸的方法
2019/08/05 PHP
PHP swoole和redis异步任务实现方法分析
2019/08/12 PHP
ThinkPHP 框架实现的读取excel导入数据库操作示例
2020/04/14 PHP
JavaScript中Cookie操作实例
2015/01/09 Javascript
JS基于MSClass和setInterval实现ajax定时采集信息并滚动显示的方法
2016/04/18 Javascript
JavaScript中boolean类型之三种情景实例代码
2016/11/21 Javascript
javascript函数的四种调用模式
2017/01/08 Javascript
Node.js获取前端ajax提交的request信息
2017/02/20 Javascript
JS实现的Unicode编码转换操作示例
2017/04/28 Javascript
详解vue-cli本地环境API代理设置和解决跨域
2017/09/05 Javascript
JavaScript for循环 if判断语句(学习笔记)
2017/10/11 Javascript
angular4 共享服务在多个组件中数据通信的示例
2018/03/30 Javascript
微信小程序实现发送验证码按钮效果
2018/12/20 Javascript
使用watch在微信小程序中实现全局状态共享
2019/06/03 Javascript
详解vuex的简单todolist例子
2019/07/14 Javascript
Python、Javascript中的闭包比较
2015/02/04 Python
python 线程的暂停, 恢复, 退出详解及实例
2016/12/06 Python
Python控制键盘鼠标pynput的详细用法
2019/01/28 Python
python实现nao机器人身体躯干和腿部动作操作
2019/04/29 Python
解决Django 在ForeignKey中出现 non-nullable field错误的问题
2019/08/06 Python
Python读写文件模式和文件对象方法实例详解
2019/09/17 Python
pygame实现贪吃蛇游戏(上)
2019/10/29 Python
HTML5给汉字加拼音收起展开组件的实现代码
2020/04/08 HTML / CSS
NBA德国官方网上商店:NBA Store德国
2018/04/13 全球购物
FLOS美国官网:意大利高级照明工艺的传奇
2018/08/07 全球购物
公司营业员的工作总结自我评价
2013/10/05 职场文书
村级环境卫生整治方案
2014/05/04 职场文书
养牛场项目建议书
2014/05/13 职场文书
2014年学习委员工作总结
2014/11/14 职场文书
《狼牙山五壮士》教学反思
2016/02/17 职场文书
Python Numpy之linspace用法说明
2021/04/17 Python
Java8 CompletableFuture 异步回调
2022/04/28 Java/Android