js基础之图片懒加载

概述

什么是图片的懒加载?简单来说就是尽可能晚地加载图片(这里的懒,指的就是一件事只有到不得不做的时候才去做)。

举个例子,当我们在逛淘宝查看商品列表时,肯定经常遇到这种情况:如果连续向上滑动商品列表,很快就会停住(就像到了列表底部一样)。这时候如果继续向上滑动,就会出现 “加载中” 的提示,随后展示出更多的商品。这就是一个典型的懒加载案例。整个商品列表并没有在进入页面时全部加载进来,而是先加载一小部分,然后在用户向上滑动时,分阶段地加载剩余列表。

那么我们为什么要进行懒加载,以及懒加载的实现原理是什么呢?请往下看:

为什么要懒加载?

对图片进行懒加载的主要目的是防止出现因为图片过多,下载时间过长而导致的页面渲染阻塞。

以淘宝的例子来说,假设某商品列表有100种商品,那么该页面就至少含有100张图片。如果每张图片的大小是20KB,那么光这100张图片就有2MB左右的大小。现在假设用户的网速为500KB/s,那么光是加载这100张图片,就需要4秒的时间。通常来说,页面内容加载完毕之前,浏览器不会进行页面的渲染,也就是说用户在4秒内不可能看到页面(再加上渲染页面和执行脚本,这个时间可能超过6秒,这段时间将出现可怕的白屏!)。

根据调查统计显示,如果一个购物网站不能在3秒内加载出来,超过半数的用户会选择离开(对淘宝来说,半数的用户也可以以亿为单位了!)。虽然现在的大多数浏览器都可以在页面未全部加载时先渲染一部分,但是要注意,在页面全部渲染完成之前,浏览器不能响应用户操作。也就是说,即使你在3秒内就看到了页面,在页面全部加载完之前也是不可点击的。

那么这个问题该如何解决呢?

懒加载就是针对该问题的一种相当优雅的解决方案。因为用户的屏幕尺寸是相当有限的,如此多的的商品不可能同时显示在可视区内,所以我们在第一次加载页面时,不再一次性下载所有图片,而是只下载用户刚进到页面时可见的那些图片(为了不让用户刚滑动不远就出现“加载中”的提示,我们通常会多加载若干张距离可视区较近的图片),而其他不可见区域的图片就用一张图片占位,示意图如下:
在这里插入图片描述
如图所示,假设用户屏幕可以显示4种商品,为了不让用户过快地需要再次加载列表,我们先加载10件商品进来,其余的90件商品全部使用一个统一的占位图片代替。这样在上面的网速下,浏览器可以在0.5秒内下载这11张图片,加上所有的渲染时间,用户可以在3秒内就看到页面,并且此时加载的页面是可点击的(因为页面的初始化加载任务已经完成,剩下的90张图片会在交互的过程中二次加载)。这样网站的使用体验就得到了提高,这就是懒加载的意义所在。

懒加载的实现原理

根据上面的描述,我们刚进入页面时,只需要加载处于可视区的商品图片即可,因此可以根据可视区高度(也就是屏幕的高度)、图片到页面顶部的距离和滑动距离三个参数来计算当前图片是否出现在可视区。计算公式也相当简单,当三者满足下面的条件时:

图片到页面顶部的距离 < 可视区高度 + 滑动距离

这时图片就是位于屏幕可视区内的,示意图如下:

在这里插入图片描述
我们需要做的就是,绑定屏幕的滚动事件,实时监听屏幕的滚动距离,然后检查所有未被加载的图片有没有进入可视区,如果进入了可视区,就给这些图片的src属性赋值,下载相应的图片。

实现步骤

假设我们有下面的若干张图片:

<!DOCTYPE html>
<html>
<head>
    <script src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.js"></script>
    <title>demo lazyload</title>
    <meta charset="utf-8">
    <style type="text/css">
        * {
            padding: 0;
            margin: 0;
            text-decoration: none;
            list-style: none;
        }
        .layout {
            margin: 0 auto;
            width: 1000px;
        }
        .lazyload img {
            width: 300px;
            height: 400px;
        }
        .img-ct {
            margin-left: -50px;
            overflow: auto;
        }
        .item {
            float: left;
            margin-left: 50px;
            margin-bottom: 30px;
        }
    </style>
</head>
<body>
<div class="lazyload">
    <div class="layout">
        <ul class="img-ct">
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
            <li class="item">
                <a href="javascript:void(0)"><img data-img="img/Tsingtao.jpg" src="img/blank.jpg"></a>
            </li>
        </ul>
    </div>
</div>
<script>
  ...
</script>
</body>
</html>

现在这些图片全部使用一张名为blank.jpg的图片占位,我们将在script内显示可视区内的图片,可视区外的图片仍然使用占位图片。我们使用一个函数lazyLoad来实现:

<script type="text/javascript">
var lazyLoad = (function(){
  var clock;
  function init(){
    $(window).on("scroll", function(){
      if (clock) {
        clearTimeout(clock);
      }
      clock = setTimeout(function(){
     	//我们在这里检查图片,显示需要显示的图片
        checkShow();
      }, 200);
    })
    //初始时需要先检查一次,在未触发滚动时显示可视区的图片
    checkShow();
  }
  
  ...
  
  return {
    init: init
  }
})()
  lazyLoad.init();

上面的代码中,我们给window绑定了一个scroll监听器,一旦发生滚动,就会调用我们的回调函数,检查哪些图片需要显示。下面我们来实现checkShow函数:

function checkShow(){
  $(".lazyload img").each(function(){
    var $cur =$(this);
    //已经显示的图片,我们使用一个自定义属性来记录,isLoaded为true表示该图片已经加载
    if($cur.attr('isLoaded')){
      return;
    }
    //shouldShow用于检查图片是否需要显示
    if(shouldShow($cur)){
      //显示图片
      showImg($cur);
    }
   })
}

下面我们需要实现检查图片是否要显示的函数,使用上面提到的公式:

function shouldShow($node){
  var scrollH = $(window).scrollTop(),
      winH = $(window).height(),
      top = $node.offset().top;
  //此时图片处于可视区(或者已经划过了可视区)
  if(top < winH + scrollH){
    return true;
  }else{
    return false;
  }
}

最后,对于需要显示的图片(返回true的图片),我们只需要将data-src中的图片地址给到src属性即可,这样图片就会被自动下载。

function showImg($node){
  //下载该图片
  $node.attr('src', $node.attr('data-img'));
  //将isLoaded置为true,表示该图片已经加载过了
  $node.attr('isLoaded', true);
}

现在,刚进入页面时,首先会检查需要显示的图片,可视区的图片被加载出来,未在可视区的图片仍被一个占位图片代替。随着页面的滚动,滚动监听函数会被触发,重新检查有没有新出现在可视区的图片,如果有,就下载这些图片。这样,随着页面的滚动,图片就被连续加载进来了。

总结

在实现细节上,懒加载有很多的版本,这里给出的只是最基本的版本,但是这些版本的基本原理还是本文用到的位置计算公式(可能会根据实际情况对参数进行调整)。如果你的网站图片量特别大,影响了网页的整体加载速度,那么你就可以考虑使用懒加载的方式提升网页的加载速度了(对于小图标,使用sprite图,或者直接使用Base64压缩后放进页面也是不错的解决方案)。实际上懒加载的思想已经渗透到了计算机的相当多领域,是一种高效的提升系统性能的方案,希望大家在日常的编程中慢慢学习这种解决问题的思路。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值