作过图片翻转效果的朋友其实都知道,要让图片轮换的时候不出现等待,最好是先让图片下载到本地,让浏览器缓存起来。这时,通常都会用到js里边的Image对象。通常的手段无非这样:chrome
经过调用preLoadImg函数,传入图片的url,就能使图片预先下载下来了。实际上,这里用到的预下载功能也和这基本一致。图片预下载下来后,经过 img的width和height属性,就能知道图片的宽和高了。可是须要考虑到,在作图片浏览器功能时,图片都是实时显示的。好比你点了显示的按钮,这 个时候才会调用上边相似的代码来加载图片。所以,若是你直接用img.width的时候,图片尚未彻底下载下来。所以,须要用一些异步的方法,等到图片 下载完毕的时候才会再对img的width和height进行调用。
实现这样的异步方法实际上不难,图片的下载完毕事件也很简单,就是简单的onload事件。所以,咱们能够写出下面的代码:浏览器
好了,再来写一个测试用例。缓存
在firefox中测试一下,发现不错,果真和预想的效果同样,在图片下载后,就会弹出图片的宽度来。不管点击多少次或者刷新结果都同样。
不过,作到这一步,先别高兴太早——还须要考虑一下浏览器的兼容性,因而,赶忙到ie里边测试一下。没错,一样弹出了图片的宽度。可是,再点击load的时候,状况就不同了,什么反应都没有了。刷新一下,也一样如此。
通过对多个浏览器版本的测试,发现ie六、opera都会这样,而firefox和safari则表现正常。其实,缘由也挺简单的,就是由于浏览器的缓存 了。当图片加载过一次之后,若是再有对该图片的请求时,因为浏览器已经缓存住这张图片了,不会再发起一次新的请求,而是直接从缓存中加载过来。对于 firefox和safari,它们视图使这两种加载方式对用户透明,一样会引发图片的onload事件,而ie和opera则忽略了这种同一性,不会引 起图片的onload事件,所以上边的代码在它们里边不能得以实现效果。
怎么办呢?最好的状况是Image能够有一个状态值代表它是否已经载入成功了。从缓存加载的时候,由于不须要等待,这个状态值就直接是代表已经下载了,而从http请求加载时,由于须要等待下载,这个值显示为未完成。这样的话,就能够搞定了。
通过一些分析,终于发现一个为各个浏览器所兼容的Image的属性——complete。因此,在图片onload事件以前先对这个值作一下判断便可。最后,代码变成以下的样子:闭包
通过这么一番折腾,总算是让各个浏览器都能知足咱们的目标了。虽然代码很简单,可是却把图片浏览器中最核心的问题解决掉了,接下来你所要作的,仅仅是图片如何呈现的问题了。异步
代码以下:函数
在网上搜索了一下相关文章,大致上都是这个思路。测试
这个方法功能是ok的,可是有一些隐患。优化
1 建立了一个临时匿名函数来做为图片的onload事件处理函数,造成了闭包。this
相信你们都看到过ie下的内存泄漏模式的文章,其中有一个模式就是循环引用,而闭包就有保存外部运行环境的能力(依赖于做用域链的实现),因此 img.onload这个函数内部又保存了对img的引用,这样就造成了循环引用,致使内存泄漏。(这种模式的内存泄漏只存在低版本的ie6中,打过补丁 的ie6以及高版本的ie都解决了循环引用致使的内存泄漏问题)。url
2 只考虑了静态图片的加载,忽略了gif等动态图片,这些动态图片可能会屡次触发onload。
要解决上面两个问题很简单,其实很简单,代码以下:
这样既能解决内存泄漏的问题,又能避免动态图片的事件屡次触发问题。
在一些相关博文中,也有人注意到了要把img.onload 设置为null,只不过期机不对,大部分文章都是在callback运行之后,才将img.onload设置为null,这样虽然能解决循环引用的问题, 可是对于动态图片来讲,若是callback运行比较耗时的话,仍是有屡次触发的隐患的。
隐患通过上面的修改后,就消除了,可是这个代码还有优化的余地:
关于这段代码,看相关博文里的叙述,缘由以下:
确实,在ie,opera下,对于缓存图片的初始状态,与firefox和safari,chrome下是不同的(有兴趣的话,能够在不一样浏览器 下,测试一下在给img的src赋值缓存图片的url以前,img的状态),可是对onload事件的触发,倒是一致的,不论是什么浏览器。产生这个问题 的根本缘由在于,img的src赋值与 onload事件的绑定,顺序不对(在ie和opera下,先赋值src,再赋值onload,由于是缓存图片,就错过了onload事件的触发)。应该 先绑定onload事件,而后再给src赋值,代码以下: