在作h5移动页面,相信你们必定碰到过页面已经打开,可是里面的图片还未加载出来的状况,这种问题虽然不影响页面的功能,可是不利于用户体验。抛开网速的缘由,解决这个问题有多方面的思路:最基本的,要从http请求合并,缓存管理,图片压缩等方面作性能优化;另外就是能够对页面里用到的全部图片作预加载的处理,当用户打开页面的时候不当即显示第一屏,而是先显示资源加载效果,等到加载完毕,再来显示页面的主内容,这样就能解决那个问题。虽然这种加载效果占用了用户的浏览时间,可是咱们能够把它作的好看有趣一点,因此也不会影响用户体验。本文实践了这种想法,提供一个很是简洁的图片预加载组件,实现简单,功能不弱,在作移动页面的时候应该对你有参考价值。javascript
效果(代码下载):css
html里面的img标签和css中background-imag等都会触发浏览器去加载相关的图片,可是若是这个图片已经加载过了的话,浏览器就会直接使用这张已经加载好的图片,从而可以瞬间在页面中渲染出来。经过javascript,建立Image对象,而后把这些对象的src属性设置成要加载的图片地址也能触发浏览器加载图片,利用这一点就能实现图片预加载的功能:在页面里首先把那些用到了相关的图片的元素给藏掉,而后用js去加载图片,等到全部图片加载完毕再把藏掉的元素显示便可。不过这仅仅是一个基本的实现思路,要完成一个功能较健壮的预加载组件,还有如下三个问题:html
1)进度问题java
因为预加载的同时,还得作一个预加载的效果,这就须要把加载的进度实时通知到外部上下文才行。关于进度有两个实现方式,第一是已加载的数据大小/总的数据大小,第二是已加载的文件数/总的文件数,在浏览器里面,采用第一种方式是不现实的,根本没有原生的办法能够作到,因此只能采用第二种。jquery
2)图片加载失败的问题浏览器
好比说有4张图片,已经加载了50%,在加载第三张的时候出错了,该不应将进度反馈成75%呢?答案是:应该。若是不这么处理的话,进度永远没法到100%,页面主内容就没机会显示了,虽然图片加载有失败的状况,可是跟加载器没有关系,也许图片自己就不存在呢?也就是说图片加载失败不该该影响加载器的功能。缓存
3)图片加载超时的问题性能优化
图片不能加载过久,不然用户一直停留在加载效果上看不到主内容,用户的等待时间不可控制地延长,致使用户体验降低,这样就有悖加载器的初衷了。因此应该给每一个图片设置一个加载的超时时间,若是在全部图片的超时时间以后,还没加载完,就应该主动放弃加载,通知外部上下文加载完毕,显示主内容。jquery插件
综合以上这些需求,本文提供的实现是:grunt
(function () { function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } /** * @param imgList 要加载的图片地址列表,['aa/asd.png','aa/xxx.png'] * @param callback 每成功加载一个图片以后的回调,并传入“已加载的图片总数/要加载的图片总数”表示进度 * @param timeout 每一个图片加载的超时时间,默认为5s */ var loader = function (imgList, callback, timeout) { timeout = timeout || 5000; imgList = isArray(imgList) && imgList || []; callback = typeof(callback) === 'function' && callback; var total = imgList.length, loaded = 0, imgages = [], _on = function () { loaded < total && (++loaded, callback && callback(loaded / total)); }; if (!total) { return callback && callback(1); } for (var i = 0; i < total; i++) { imgages[i] = new Image(); imgages[i].onload = imgages[i].onerror = _on; imgages[i].src = imgList[i]; } /** * 若是timeout * total时间范围内,仍有图片未加载出来(判断条件是loaded < total),通知外部环境全部图片均已加载 * 目的是避免用户等待时间过长 */ setTimeout(function () { loaded < total && (loaded = total, callback && callback(loaded / total)); }, timeout * total); }; "function" === typeof define && define.cmd ? define(function () { return loader }) : window.imgLoader = loader; })();
使用方式(对应代码中的test.html):
<script src="../js/imgLoader.js"></script> <script> imgLoader(['../img/page1.jpg', '../img/page2.jpg', '../img/page3.jpg'], function(percentage){ console.log(percentage) }); </script>
运行结果:
本文开篇给出的效果,对应的页面是index.html,关于这个效果还有两个问题须要说明:
1)它用了以前这篇博客利用轮播原理结合hammer.js实现简洁的滑屏功能介绍的滑屏思路,并把它的一些逻辑包装在了swipe.js,对外提供了一个全局变量Swipe,这个模块有一个init的方法,以便外部经过调用Swipe.init()就能初始化滑屏相关的功能,原来没有提供这个init方法,在js加载完毕就会初始化滑屏功能,有了这个init方法就能够把滑屏的逻辑延迟到加载完毕的时候去初始化。index.html一共引用了5个js:
<script src="js/zepto.js"></script> <script src="js/transition.js"></script> <script src="js/hammer.js"></script> <script src="js/imgLoader.js"></script> <script src="js/swipe.js"></script>
其中imgLoader.js就是前面介绍图片加载器的实现,前三个js都是为最后一个swipe.js服务的,感兴趣的能够继续个人博客利用轮播原理结合hammer.js实现简洁的滑屏功能了解相关内容。不过滑屏不是本文的重点,不了解swipe.js不会影响理解本文的内容~
2)虽然我在demo中用到了3张比较大的图片,可是因为在本地环境,加载速度仍是很是快,因此一开始的时候,很难看到预加载的效果,最后只能想办法在每一个进度回调以前作一下延迟,这才能够看到前面gif图片一开始的那个loading效果,实现方式是:
//模拟加载慢的效果 var callbacks = []; imgLoader(['img/page1.jpg', 'img/page2.jpg', 'img/page3.jpg'], function (percentage) { var i = callbacks.length; callbacks.push(function(){ setTimeout(function(){ var percentT = percentage * 100; $('#loader__info').html('Loading ' + (parseInt(percentT)) + '%'); $('#loader__progress')[0].style.width = percentT + '%'; if (percentage == 1) { setTimeout(function(){ $('#loader').remove(); Swipe.init(); }, 600); } callbacks[i + 1] && callbacks[i + 1](); },600); }); if(percentage == 1) { callbacks[0](); } });
在真实环境,最好仍是不要刻意去加这种延迟,不必为了让用户看到一个好看有趣的加载效果,就浪费它没必要要的等待时间,因此真实环境仍是应该用下面的代码:
imgLoader(['img/page1.jpg', 'img/page2.jpg', 'img/page3.jpg'], function (percentage) { var percentT = percentage * 100; $('#loader__info').html('Loading ' + (parseInt(percentT)) + '%'); $('#loader__progress')[0].style.width = percentT + '%'; if (percentage == 1) { $('#loader').remove(); Swipe.init(); } });
另外运行demo,须要用到grunt启动静态服务,若是已经安装好grunt-cli,则直接运行grunt connect任务便可打开demo的index.html。
预加载是一种比较常见的实现效果,可是在使用的时候,有些问题须要注意:
1)何时用
页面大的时候用,通常页面大小超过3M就该考虑使用;页面内包含数据量比较大的图片,在手机端测试可以明显看到加载缓慢的时候,能够考虑使用。
2)尽可能使用sprite图片
3)加载效果实现的时候,尽可能不用图片,即便要用也应该用很小的图片,不然加载效果卡在那就没有意义了。
本文主要介绍了一个简单的图片预加载器,可应用于h5移动页面的开发当中,在它的思路之下,若是有必要的话,还能够对它进行一些改造,用它来加载其它类型的资源,好比音频或者视频文件,毕竟这些类型的DOM对象也都有提供相似Image对象的属性和回调。与预加载的方式相反的,还有一种图片懒加载的技术,如今网上已经有比较好用的jquery插件了,不过仍是很值的去深刻了解下它的思路跟实现要点,等我有时间去研究研究再写博客来介绍,敬请关注!