基于纯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 相关文章推荐
jQuery 图像裁剪插件Jcrop的简单使用
May 22 Javascript
jquery 弹出层实现代码
Oct 30 Javascript
jQuery学习笔记之jQuery的动画
Dec 22 Javascript
js禁止页面刷新禁止用F5键刷新禁止右键的示例代码
Sep 23 Javascript
JQuery的$和其它JS发生冲突的快速解决方法
Jan 24 Javascript
jQuery中事件对象e的事件冒泡用法示例介绍
Apr 25 Javascript
JS实现转动随机数抽奖特效代码
Apr 16 Javascript
Vue.js实现简单动态数据处理
Feb 13 Javascript
js,jq,css多方面实现简易下拉菜单功能
May 13 Javascript
AngularJS中filter的使用实例详解
Aug 25 Javascript
简单易扩展可控性强的Jquery转盘抽奖程序
Mar 16 jQuery
vue实现滑动切换效果(仅在手机模式下可用)
Jun 29 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 快速生成 Flash 动画的方法
2007/03/06 PHP
解析func_num_args与func_get_args函数的使用
2013/06/24 PHP
PHP 命名空间和自动加载原理与用法实例分析
2020/04/29 PHP
比较简单的一个符合web标准的JS调用flash方法
2007/11/29 Javascript
js 颜色选择器(兼容firefox)
2009/03/05 Javascript
JavaScript(js)设置默认输入焦点(focus)
2012/12/28 Javascript
JavaScript中的关键字&quot;VAR&quot;使用详解 分享
2013/07/31 Javascript
javascript中字符串的定义示例代码
2013/12/19 Javascript
利用JS判断用户是否上网(连接网络)
2013/12/23 Javascript
JQuery调用WebServices的方法和4个实例
2014/05/06 Javascript
JS实现网页背景颜色与select框中颜色同时变化的方法
2015/02/27 Javascript
使用Jquery实现每日签到功能
2015/04/03 Javascript
JS制作手机端自适应缩放显示
2015/06/11 Javascript
js Canvas实现圆形时钟教程
2016/09/19 Javascript
vue修改vue项目运行端口号的方法
2017/08/04 Javascript
快速将Vue项目升级到webpack3的方法步骤
2017/09/14 Javascript
vue awesome swiper异步加载数据出现的bug问题
2018/07/03 Javascript
Vue CLI 3.x 自动部署项目至服务器的方法
2019/04/02 Javascript
SSM+layUI 根据登录信息显示不同的页面方法
2019/09/20 Javascript
ES6 十大特性简介
2020/12/09 Javascript
python中的一些类型转换函数小结
2013/02/10 Python
详解使用python crontab设置linux定时任务
2016/12/08 Python
python多进程并行代码实例
2019/09/30 Python
python使用beautifulsoup4爬取酷狗音乐代码实例
2019/12/04 Python
python实现数字炸弹游戏
2020/07/17 Python
Python 使用Opencv实现目标检测与识别的示例代码
2020/09/08 Python
python实现学生通讯录管理系统
2021/02/25 Python
HTML 5 标签、属性、事件及浏览器兼容性速查表 附打包下载
2012/10/20 HTML / CSS
html5 自定义播放器核心代码
2013/12/20 HTML / CSS
Hammitt官网:设计师手袋
2020/05/23 全球购物
餐饮业经理竞聘演讲稿
2014/01/14 职场文书
球队口号
2014/06/18 职场文书
《周恩来的四个昼夜》观后思想汇报范文两篇
2014/09/10 职场文书
群众路线党员个人整改措施
2014/10/27 职场文书
大学生学生会工作总结2015
2015/05/26 职场文书
2015年工会工作总结范文
2015/07/23 职场文书