zoukankan      html  css  js  c++  java
  • 图片懒加载的几种优秀的方式(1)

    原生 JS 实现最简单的图片懒加载 

     

    懒加载


    什么是懒加载

    懒加载其实就是延迟加载

    是一种对网页性能优化的方式

    比如当访问一个页面的时候

    优先显示可视区域的图片而不一次性加载所有图片

    当需要显示的时候再发送图片请求

    避免打开网页时加载过多资源。

    为什么要把上面的话分开来写呢,因为上面的话相当简洁,每一句话都是一个操作或者是一个关键点,整体概括了整个图片懒加载的整个流程和优点。而我们会用到图片懒加载的情况就是图片过多的情况。

    懒加载原理及实现原理

    <img>标签有一个属性是 src,用来表示图像的URL,当这个属性的值不为空时,浏览器就会根据这个值发送请求。如果没有 src属性,就不会发送请求。

    我们先不给 <img>设置 src,把图片真正的URL放在另一个属性 data-src中,在需要的时候也就是图片进入可视区域的之前,将URL取出放到 src中。

    具体列子


    HTML结构

    1. <div class="container">

    2.  <div class="img-area">

    3.    <img class="my-photo" alt="loading" data-src="./img/img1.png">

    4.  </div>

    5.  <div class="img-area">

    6.    <img class="my-photo" alt="loading" data-src="./img/img2.png">

    7.  </div>

    8.  <div class="img-area">

    9.    <img class="my-photo" alt="loading" data-src="./img/img3.png">

    10.  </div>

    11.  <div class="img-area">

    12.    <img class="my-photo" alt="loading" data-src="./img/img4.png">

    13.  </div>

    14.  <div class="img-area">

    15.    <img class="my-photo" alt="loading" data-src="./img/img5.png">

    16.  </div>

    17. </div>

    仔细观察一下, <img>标签此时是没有 src属性的,只有 altdata-src属性。

    data-* 全局属性:构成一类名称为自定义数据属性的属性,可以通过 HTMLElement.dataset来访问

    判断元素是否在可视区域的方法:

    1. 通过 document.documentElement.clientHeight获取屏幕可视窗口高度

    2. 通过 document.documentElement.scrollTop获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离

    3. 通过 element.offsetTop获取元素相对于文档顶部的距离

    然后判断②-③<①是否成立,如果成立,元素就在可视区域内。

    方法二(推荐)

    通过 getBoundingClientRect()方法来获取元素的大小以及位置,MDN上是这样描述的:

    The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.

    这个方法返回一个名为 ClientRectDOMRect对象,包含了 toprightbottomleftwidthheight这些值。

    MDN上有这样一张图:


                                top

                                ↑

                     left←   ClientRect的 DOMRect     →ight 

                                ↓

                                 bottom


    假设 const bound=el.getBoundingClientRect();来表示图片到可视区域顶部距离;(top)

    并设 const clientHeight=window.innerHeight;来表示可视区域的高度。

    随着滚动条的向下滚动, bound.top会越来越小,也就是图片到可视区域顶部的距离越来越小,当 bound.top===clientHeight时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。

    也就是说,在 bound.top<=clientHeight时,图片是在可视区域内的。

    我们这样判断:

    1. function isInSight(el) {

    2.    const bound = el.getBoundingClientRect();

    3.    const clientHeight = window.innerHeight;

    4.    //如果只考虑向下滚动加载

    5.    //const clientWidth = window.innerWeight;

    6.    return bound.top <= clientHeight + 100;

    7. }

    这里有个+100是为了提前加载。

    加载图片

    页面打开时需要对所有图片进行检查,是否在可视区域内,如果是就加载。

    1. function checkImgs() {

    2.  const imgs = document.querySelectorAll('.my-photo');// 获取所有的图片

    3.  Array.from(imgs).forEach(el => {// 将imgs转化为数组并且遍历

    4.    if (isInSight(el)) {

    5.        loadImg(el);

    6.    }

    7.  })

    8. }

    9. function loadImg(el) {

    10.  if (!el.src) {

    11.    const source = el.dataset.src;

    12.    el.src = source;

    13.  }

    14. }

    这里应该是有一个优化的地方,设一个标识符标识已经加载图片的index,当滚动条滚动时就不需要遍历所有的图片,只需要遍历未加载的图片即可。// 这个优化很强劲

    函数节流

    在类似于滚动条滚动等频繁的DOM操作时,总会提到“函数节流、函数防抖”。

    所谓的函数节流,也就是让一个函数不要执行的太频繁,减少一些过快的调用来节流。

    基本步骤:

    1. 获取第一次触发事件的时间戳

    2. 获取第二次触发事件的时间戳

    3. 时间差如果大于某个阈值就执行事件,然后重置第一个时间

    1. function throttle(fn, mustRun = 500) {//  es6

    2.  const timer = null;

    3.  let previous = null;

    4.  return function() {

    5.    const now = new Date();

    6.    const context = this;

    7.    const args = arguments;

    8.    if (!previous){// 第一次执行的时间

    9.      previous = now;

    10.    }

    11.    const remaining = now - previous;// 时间差

    12.    if (mustRun && remaining >= mustRun) {//  时间大雨500或者执行时间大约500就执行

    13.      fn.apply(context, args);

    14.      previous = now;//  更新时间

    15.    }

    16.  }

    17. }

    这里的 mustRun就是调用函数的时间间隔,无论多么频繁的调用 fn,只有 remaining>=mustRunfn才能被执行。

     

    更新


     

    IntersectionObserver可以自动观察元素是否在视口内。

    1. var io = new IntersectionObserver(callback, option);

    2. // 开始观察

    3. io.observe(document.getElementById('example'));

    4. // 停止观察

    5. io.unobserve(element);

    6. // 关闭观察器

    7. io.disconnect();

    callback的参数是一个数组,每个数组都是一个 IntersectionObserverEntry对象,包括以下属性:

    属性描述
    time 可见性发生变化的时间,单位为毫秒
    rootBounds 与getBoundingClientRect()方法的返回值一样
    boundingClientRect 目标元素的矩形区域的信息
    intersectionRect 目标元素与视口(或根元素)的交叉区域的信息
    intersectionRatio 目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
    target 被观察的目标元素,是一个 DOM 节点对象

    我们需要用到 intersectionRatio来判断是否在可视区域内,当 intersectionRatio && intersectionRatio <= 1即在可视区域内。

    代码

    1. function checkImgs() {

    2.  const imgs = Array.from(document.querySelectorAll(".my-photo"));

    3.  imgs.forEach(item => io.observe(item));

    4. }

    5. function loadImg(el) {

    6.  if (!el.src) {

    7.    const source = el.dataset.src;

    8.    el.src = source;

    9.  }

    10. }

    11. const io = new IntersectionObserver(ioes => {

    12.  ioes.forEach(ioes => {

    13.    const el = ioes.target;

    14.    const intersectionRatio = ioes.intersectionRatio;

    15.    if (intersectionRatio > 0 && intersectionRatio <= 1) {

    16.      loadImg(el);

    17.    }

    18.    el.onload = el.onerror = () => io.unobserve(el);

    19.  });

    20. });

    上面的几种图片懒加载的方法都很优秀,很值得学习,尤其是最后一种,长见识了。2017-08-28

     
  • 相关阅读:
    例行性工作排程 (crontab)
    数组
    继续我们的学习。这次鸟哥讲的是LVM。。。磁盘管理 最后链接文章没有看
    htop资源管理器
    转:SSL协议详解
    转:SSL 握手协议详解
    转:Connection reset原因分析和解决方案
    使用Mybatis-Generator自动生成Dao、Model、Mapping相关文件(转)
    转:logback的使用和logback.xml详解
    转:Java logger组件:slf4j, jcl, jul, log4j, logback, log4j2
  • 原文地址:https://www.cnblogs.com/wyliunan/p/7445611.html
Copyright © 2011-2022 走看看