马海祥博客是一个专注于分享SEO优化、网站制作、网络营销和运营思维的自媒体博客!
马海祥博客 > 网站制作 > 网页制作 > 实现网页图片预加载效果的3种技术方法

实现网页图片预加载效果的3种技术方法

时间:2014-05-02   文章来源:马海祥博客   访问次数:

网站开发时经常需要在某个页面需要实现对大量图片的浏览,如果考虑流量的话,大可每个页面只显示一张图片,让用户每看一张图片就需要重新下载一下整个页面。不过,在web2.0时代,更多人愿意用javascript来实现一个图片浏览器,让用户无需等待过长的时间就能看到其他图片。所以,对于一个网站来说,网页的预加载就显得尤为重要。在此,马海祥从网上找了3种预加载的代码,就在马海祥博客上跟大家分享一下,共同分析一下:

实现网​页​图片预加载效果的3种技术方法-马海祥博客

一、常规实现图片预加载的方法

知道了一张图片的地址,需要把它在一个固定大小的html容器(可以是div等)里边显示出来,最重要的当然是需要知道这张即将显示的图片的宽和高,然后再结合容器的宽和高,按照一定的缩放比例使图片显示出来。因此,实现图片预加载就成为图片浏览器的核心功能了。

做过图片翻转效果的朋友其实都知道,要让图片轮换的时候不出现等待,最好是先让图片下载到本地,让浏览器缓存起来。这时,一般都会用到js里边的Image对象。一般的手段无非这样:

function preLoadImg(url) {
   var img = new Image();
   img.src = url;
  }

通过调用preLoadImg函数,传入图片的url,就能使图片预先下载下来了。实际上,马海祥觉得这里用到的预下载功能也和这基本一致。图片预下载下来后,通过img的width和height属性,就能知道图片的宽和高了。

但是需要考虑到,在做图片浏览器功能时,图片都是实时显示的。比如你点了显示的按钮,这个时候才会调用上边类似的代码来加载图片。因此,如果你直接用img.width的时候,图片还没有完全下载下来。因此,需要用一些异步的方法,等到图片下载完毕的时候才会再对img的width和height进行调用。

实现这样的异步方法实际上不难,图片的下载完毕事件也很简单,就是简单的onload事件。因此,我们可以利用下面的代码:

function loadImage(url, callback) {
    var img = new Image();
     img.src = url;
     img.onload = function(){ //图片下载完毕时异步调用callback函数。
       callback.call(img);   // 将callback函数this指针切换为img。
     };
  }

好了,再来写一个测试用例:

function imgLoaded(){
     alert(this.width);
  }
  <input type="button" value="loadImage" onclick="loadImage('aaa.jpg',imgLoaded)"/>

在firefox中测试一下,发现不错,果然和预想的效果一样,在图片下载后,就会弹出图片的宽度来。无论点击多少次或者刷新结果都一样。

不过,做到这一步,先别高兴太早——还需要考虑一下浏览器的兼容性,于是,赶紧到ie里边测试一下。没错,同样弹出了图片的宽度。但是,再点击load的时候,情况就不一样了,什么反应都没有了。刷新一下,也同样如此。

经过对多个浏览器版本的测试,发现ie6、opera都会这样,而firefox和safari则表现正常。其实,原因也挺简单的,就是因为浏览器的缓存了。当图片加载过一次以后,如果再有对该图片的请求时,由于浏览器已经缓存住这张图片了,不会再发起一次新的请求,而是直接从缓存中加载过来。

对于firefox和safari,它们视图使这两种加载方式对用户透明,同样会引起图片的onload事件,而ie和opera则忽略了这种同一性,不会引起图片的onload事件,因此上边的代码在它们里边不能得以实现效果。

怎么办呢?最好的情况是Image可以有一个状态值表明它是否已经载入成功了。从缓存加载的时候,因为不需要等待,这个状态值就直接是表明已经下载了,而从http请求加载时,因为需要等待下载,这个值显示为未完成。这样的话,就可以搞定了。

经过一些分析,马海祥终于发现一个为各个浏览器所兼容的Image的属性——complete。所以,在图片onload事件之前先对这个值做一下判断即可。最后,代码变成如下的样子:

function loadImage(url, callback) {
      var img = new Image(); //创建一个Image对象,实现图片的预下载
       img.src = url;
      if (img.complete) { // 如果图片已经存在于浏览器缓存,直接调用回调函数
           callback.call(img);
          return; // 直接返回,不用再处理onload事件
       }
       img.onload = function () { //图片下载完毕时异步调用callback函数。
           callback.call(img);//将回调函数的this替换为Image对象
       };
  };

经过这么一番折腾,总算是让各个浏览器都能满足我们的目标了。虽然代码很简单,但是却把图片浏览器中最核心的问题解决掉了,接下来你所要做的,仅仅是图片如何呈现的问题了,当然,也可以通过css来实现图片的展示效果,具体可查看马海祥博客的《CSS无图片技术的实现方法有哪些》,在此,我就不多说了。

二、动态图片的预加载技术

一般来说,技术人员在实现图片预加载的大体思路都是这样的:

function loadImage(url, callback) {    
      var img = new Image(); //创建一个Image对象,实现图片的预下载    
      img.src = url;         
      if (img.complete) { // 如果图片已经存在于浏览器缓存,直接调用回调函数    
          callback(img);    
          return; // 直接返回,不用再处理onload事件    
      }     
   img.onload = function () { //图片下载完毕时异步调用callback函数。        
       callback(img);    
    };  
  };

马海祥觉得这个方法功能是ok的,但是有一些隐患,具体如下:

1、创建了一个临时匿名函数来作为图片的onload事件处理函数,形成了闭包。

相信大家都看到过ie下的内存泄漏模式的文章,其中有一个模式就是循环引用,而闭包就有保存外部运行环境的能力(依赖于作用域链的实现),所以img.onload这个函数内部又保存了对img的引用,这样就形成了循环引用,导致内存泄漏。(这种模式的内存泄漏只存在低版本的ie6中,打过补丁的ie6以及高版本的ie都解决了循环引用导致的内存泄漏问题)。

2、只考虑了静态图片的加载,忽略了gif等动态图片,这些动态图片可能会多次触发onload。

要解决上面两个问题很简单,其实很简单,代码如下:

img.onload = function () { //图片下载完毕时异步调用callback函数。        
      img.onload = null;   
      callback(img);    
  };

这样既能解决内存泄漏的问题,又能避免动态图片的事件多次触发问题。

在一些相关博文中,也有人注意到了要把img.onload 设置为null,只不过时机不对,大部分文章都是在callback运行以后,才将img.onload设置为null,这样虽然能解决循环引用的问题,但是对于动态图片来说,如果callback运行比较耗时的话,还是有多次触发的隐患的。

隐患经过上面的修改后,就消除了,但是这个代码还有优化的余地:

if (img.complete) { // 如果图片已经存在于浏览器缓存,直接调用回调函数    
        callback(img);    
        return; // 直接返回,不用再处理onload事件    
  }

关于这段代码,马海祥觉得其原因如下:

经过对多个浏览器版本的测试,发现ie、opera下,当图片加载过一次以后,如果再有对该图片的请求时,由于浏览器已经缓存住这张图片了,不会再发起一次新的请求,而是直接从缓存中加载过来。对于 firefox和safari,它们试图使这两种加载方式对用户透明,同样会引起图片的onload事件,而ie和opera则忽略了这种同一性,不会引起图片的onload事件,因此上边的代码在它们里边不能得以实现效果。

确实,在ie,opera下,对于缓存图片的初始状态,与firefox和safari,chrome下是不一样的(有兴趣的话,可以在不同浏览器下,测试一下在给img的src赋值缓存图片的url之前,img的状态),但是对onload事件的触发,却是一致的,不管是什么浏览器。产生这个问题的根本原因在于,img的src赋值与 onload事件的绑定,顺序不对(在ie和opera下,先赋值src,再赋值onload,因为是缓存图片,就错过了onload事件的触发)。应该先绑定onload事件,然后再给src赋值,代码如下:

function loadImage(url, callback) {    
      var img = new Image(); //创建一个Image对象,实现图片的预下载    
      img.onload = function(){
          img.onload = null;
          callback(img);
      }
      img.src = url;
  }

这样内存泄漏,动态图片的加载问题都得到了解决(关于图片的优化分类可查看马海祥博客的《网站图片优化的分类有哪些》相关介绍),而且也以统一的方式,实现了callback的调用。   

三、比onload更快获取图片尺寸的预加载技术

大部分技术人员使用预加载获取图片大小的方法,基本都是通过如下的代码实现的:

var imgLoad = function (url, callback) {
      var img = new Image();
      img.src = url;
      if (img.complete) {
          callback(img.width, img.height);
      } else {
          img.onload = function () {
              callback(img.width, img.height);
              img.onload = null;
          };
      };
  };

从以上代码,我们可以看到上面必须等待图片加载完毕才能获取尺寸,其速度马海祥还真不敢恭维,对此,我们需要改进。

web应用程序区别于桌面应用程序,响应速度才是最好的用户体验。如果想要速度与优雅兼得,那就必须提前获得图片尺寸,如何在图片没有加载完毕就能获取图片尺寸呢?

据马海祥十多年的上网经验:浏览器在加载图片的时候你会看到图片会先占用一块地然后才慢慢加载完毕,并且不需要预设width与height属性,因为浏览器能够获取图片的头部数据。基于此,只需要使用javascript定时侦测图片的尺寸状态便可得知图片尺寸就绪的状态。

当然实际中会有一些兼容陷阱,如width与height检测各个浏览器的不一致,还有webkit new Image()建立的图片会受以处在加载进程中同url图片影响,经过反复测试后的最佳处理方式:

// 更新:
  // 05.27: 1、保证回调执行顺序:error > ready > load;2、回调函数this指向img本身
  // 04-02: 1、增加图片完全加载后的回调 2、提高性能
  /**
   * 图片头数据加载就绪事件 - 更快获取图片尺寸
   * @version 2011.05.27
   * @author  TangBin
   * @see    http://www.mahaixiang.cn/wyzz/546.html
   * @param   {String}    图片路径
   * @param   {Function}  尺寸就绪
   * @param   {Function}  加载完毕 (可选)
   * @param   {Function}  加载错误 (可选)
   * @example imgReady('http://www.mahaixiang.cn/uploads/allimg/1405/1-14050212013ML.jpg', function () {
          alert('size ready: width=' + this.width + '; height=' + this.height);
      });
   */
var imgReady = (function () {
    var list = [], intervalId = null,
    // 用来执行队列
    tick = function () {
        var i = 0;
        for (; i < list.length; i++) {
            list[i].end ? list.splice(i--, 1) : list[i]();
        };
        !list.length && stop();
    },
    // 停止所有定时器队列
    stop = function () {
        clearInterval(intervalId);
        intervalId = null;
    };
    return function (url, ready, load, error) {
        var onready, width, height, newWidth, newHeight,
            img = new Image();
        img.src = url;
        // 如果图片被缓存,则直接返回缓存数据
        if (img.complete) {
            ready.call(img);
            load && load.call(img);
            return;
        };
        width = img.width;
        height = img.height;
        // 加载错误后的事件
        img.onerror = function () {
            error && error.call(img);
            onready.end = true;
            img = img.onload = img.onerror = null;
        };
        // 图片尺寸就绪
        onready = function () {
            newWidth = img.width;
            newHeight = img.height;
            if (newWidth !== width || newHeight !== height ||
                // 如果图片已经在其他地方加载可使用面积检测
                newWidth * newHeight > 1024
            ) {
                ready.call(img);
                onready.end = true;
            };
        };
        onready();
        // 完全加载完毕的事件
        img.onload = function () {
            // onload在定时器时间差范围内可能比onready快
            // 这里进行检查并保证onready优先执行
            !onready.end && onready();
            load && load.call(img);
            // IE gif动画会循环执行onload,置空onload即可
            img = img.onload = img.onerror = null;
        };
        // 加入队列中定期执行
        if (!onready.end) {
            list.push(onready);
            // 无论何时只允许出现一个定时器,减少浏览器性能损耗
            if (intervalId === null) intervalId = setInterval(tick, 40);
        };
    };
})();

调用例子:

imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () {
      alert('size ready: width=' + this.width + '; height=' + this.height);
  });

是不是很简单?这样的方式获取摄影级别照片尺寸的速度往往是onload方式的几十多倍,而对于web普通(800×600内)浏览级别的图片能达到秒杀效果。看了这个再回忆一下你见过的web相册,是否绝大部分都可以重构一下的。

马海祥博客点评:

预加载图片是一个很好提升用户体验的小激情,以及让自己的网站看起来更专业的一个途径,进度条可以让用户知道加载的进展,避免用户的冷落感,这也能显著提升你的网站形象。

本文发布于马海祥博客文章,如想转载,请注明原文网址摘自于http://www.mahaixiang.cn/wyzz/546.html,注明出处;否则,禁止转载;谢谢配合!

相关标签搜索: 图片预加载   图片效果   预加载技术   预加载方法   预加载  

上一篇:网页制作过程中常用的20个功能代码
下一篇:移动端手机网站页面制作的25个设计要点

您可能还会对以下这些文章感兴趣!

  • 新手制作网页过程中必知的10个要点

    一个好的网站网页不光只是看起来好看而已,还要是用户友好型的,通常来说,一个干净、简单的网页设计最终会成为一个可用性高的网页设计,因为它在与用户的交互中不会使其产生混淆,当页面上有太多网站功能和组件时,将会分散网站用户的注意力而失去原本浏览网站的目的……【查看全文

    阅读:2242关键词: 制作网页   网页制作   日期:2014-11-30
  • 网站制作之前构建开发环境的具体操作步骤

    在制作网站,特别是开发网站数据库之前,首先应该认真构建好Dreamweaver MX 2004+ASP.NET开发环境。对于学习的新手,由于网上的配置方法都是很久之前的,及时性不高,环境配置往往令人很头疼,我初学php配置环境时配置了2个晚上,每次总是会出现这样或那样的问题,所以……【查看全文

    阅读:787关键词: 网站制作   日期:2017-06-27
  • 网页制作过程中常用的20个功能代码

    一个好的网页制作要能充分吸引访问者的注意力,让访问者产生视觉上的愉悦感。因此在网页创作的时候就必须将网站的整体设计与网页设计的相关原理紧密结合起来。那么在网页制作过程中有哪些代码功能能使我们的网站更加的个性化呢? 1、让背景图不滚动 IE浏览器支持一个Body属性……【查看全文

    阅读:2688关键词: 网页制作   网页功能代码   网页代码   日期:2014-01-13
  • 实现网页图片预加载效果的3种技术方法

    网站开发时经常需要在某个页面需要实现对大量图片的浏览,如果考虑流量的话,大可每个页面只显示一张图片,让用户每看一张图片就需要重新下载一下整个页面。不过,在web2.0时代,更多人愿意用js来实现一个图片浏览器,让用户无需等待过长的时间就能看到其他图片……【查看全文

    阅读:10775关键词: 图片预加载   图片效果   预加载技术   预加载方法   预加载   日期:2014-05-02
  • 如何制作一个响应式网页?

    总体来说,响应式网页设计是一种新的网页设计思路,我们需要满足不同设备下的浏览模式,达到俱佳的体验效果。所以要想做好一个响应式网页,首先必须要满足桌面各个浏览器版本下正常显示页面。其次才考虑以webkit为内核的手机终端浏览器效果。由于IOS和Android浏览器都是……【查看全文

    阅读:4968关键词: 响应式网页   网页制作   如何制作网页   响应式设计   日期:2013-09-18
  • 网页制作中代码关于seo的18个小技巧

    网页设计 是指使用标识语言(markup language),通过一系列设计、建模、和执行的过程将电子格式的信息通过互联网传输,最终以图形用户界面(GUI)的形式被用户所浏览。 随着浏览器和W3C标准一致性的改善,XHTML/XML(可扩展标识语言)与CSS(层叠样式表)共同用作网页内……【查看全文

    阅读:971关键词: 网页制作   网页制作代码   seo技巧   日期:2012-10-09
  • 提高网站速度的6种网站前端优化方法

    最近有几个同事开通了个人博客,但却抱怨说因为的买的虚拟空间,所以个人博客网站打开速度很慢。对于这种现象,按照一般的情况来看,一个网站的访问打开速度的快与慢,全是看这个网站使用的空间配置,要是这个空间主机的配置不错,那么你的网站访问速度就会很快;相反,……【查看全文

    阅读:4974关键词: 提高网站速度   网站前端优化   优化方法   日期:2013-05-06
  • 提高网页加载速度的一些方法和技巧

    许多研究发现,页面速度和访客的滞留时间,跳出率以及收入都有直接的关系。另外,谷歌的排名算法中也把页面加载速度作为其中一项考虑因素,因此,你网站的页面加载时间是至关重要的,从访问者的角度看,测试你的浏览器速度的一个很好的方法是清除你的浏览器缓存,然后加……【查看全文

    阅读:6368关键词: 网页速度   加载速度   日期:2016-07-24
  • 如何创建网站或应用程序的样式指南和标准

    一般来说,专业的设计师在每个项目开始之前就会创建一套网站的设计指南与标准,这样可以优化工作流程,使设计保持统一,是每个严谨的设计该有的职业态度,也是成为专业设计师的奠基石之一。为每个项目创建指南可以优化你的工作流程,也能使你的设计保持统一,并采取正确……【查看全文

    阅读:1039关键词: 网站标准   应用程序   样式指南   标准样式   日期:2014-06-13
  • 如何利用dreamweaver工具批量制作网页模板

    对于网页模板的出现,可以说在很大程度上节省了美工和程序人员的工作时间,并大幅度提高了工作效率,一个合格的网页模板的确让我们可以有一条更好的路径对待一些低端的客户,能够快速的去完成我们的一些任务,而且这也是网页模板的理念所在。特别是大规模对网站进行更新……【查看全文

    阅读:1877关键词: dreamweaver   制作网页   网页模板   日期:2014-06-28
↓ 点击查看更多 ↓

互联网更多>>

SEO优化 更多>>

企业网站SEO推广能给企业带来哪些好处? 百度搜索用户建议专区上线公告