利用简洁的图片预加载组件提升html5移动页面的用户体验


Posted in HTML / CSS onMarch 11, 2016

在做h5移动页面,相信大家一定碰到过页面已经打开,但是里面的图片还未加载出来的情况,这种问题虽然不影响页面的功能,但是不利于用户体验。抛开网速的原因,解决这个问题有多方面的思路:最基本的,要从http请求合并,缓存管理,图片压缩等方面做性能优化;另外就是可以对页面里用到的所有图片做预加载的处理,当用户打开页面的时候不立即显示第一屏,而是先显示资源加载效果,等到加载完毕,再来显示页面的主内容,这样就能解决那个问题。虽然这种加载效果占用了用户的浏览时间,但是我们可以把它做的好看有趣一点,所以也不会影响用户体验。本文实践了这种想法,提供一个非常简洁的图片预加载组件,实现简单,功能不弱,在做移动页面的时候应该对你有参考价值。
效果:

利用简洁的图片预加载组件提升html5移动页面的用户体验

1. 实现思路

html里面的img标签和css中background-imag等都会触发浏览器去加载相关的图片,但是如果这个图片已经加载过了的话,浏览器就会直接使用这张已经加载好的图片,从而能够瞬间在页面中渲染出来。通过javascript,创建Image对象,然后把这些对象的src属性设置成要加载的图片地址也能触发浏览器加载图片,利用这一点就能实现图片预加载的功能:在页面里首先把那些用到了相关的图片的元素给藏掉,然后用js去加载图片,等到所有图片加载完毕再把藏掉的元素显示即可。不过这仅仅是一个基本的实现思路,要完成一个功能较健壮的预加载组件,还有以下三个问题:
1)进度问题
由于预加载的同时,还得做一个预加载的效果,这就需要把加载的进度实时通知到外部上下文才行。关于进度有两个实现方式,第一是已加载的数据大小/总的数据大小,第二是已加载的文件数/总的文件数,在浏览器里面,采用第一种方式是不现实的,根本没有原生的办法可以做到,所以只能采用第二种。
2)图片加载失败的问题
比如说有4张图片,已经加载了50%,在加载第三张的时候出错了,该不该将进度反馈成75%呢?答案是:应该。如果不这么处理的话,进度永远无法到100%,页面主内容就没机会显示了,虽然图片加载有失败的情况,但是跟加载器没有关系,也许图片本身就不存在呢?也就是说图片加载失败不应该影响加载器的功能。
3)图片加载超时的问题
图片不能加载太久,否则用户一直停留在加载效果上看不到主内容,用户的等待时间不可控制地延长,导致用户体验下降,这样就有悖加载器的初衷了。所以应该给每个图片设置一个加载的超时时间,如果在所有图片的超时时间之后,还没加载完,就应该主动放弃加载,通知外部上下文加载完毕,显示主内容。
综合以上这些需求,本文提供的实现是:

JavaScript Code复制内容到剪贴板
  1. (function () {    
  2. function isArray(obj) {    
  3. return Object.prototype.toString.call(obj) === '[object Array]';    
  4. }    
  5. /**   
  6. * @param imgList 要加载的图片地址列表,['aa/asd.png','aa/xxx.png']   
  7. * @param callback 每成功加载一个图片之后的回调,并传入“已加载的图片总数/要加载的图片总数”表示进度   
  8. * @param timeout 每个图片加载的超时时间,默认为5s   
  9. */    
  10. var loader = function (imgList, callback, timeout) {    
  11. timeout = timeout || 5000;    
  12. imgList = isArray(imgList) && imgList || [];    
  13. callback = typeof(callback) === 'function' && callback;    
  14. var total = imgList.length,    
  15. loaded = 0,    
  16. imgages = [],    
  17. _on = function () {    
  18. loaded < total && (++loaded, callback && callback(loaded / total));    
  19. };    
  20. if (!total) {    
  21. return callback && callback(1);    
  22. }    
  23. for (var i = 0; i < total; i++) {    
  24. imgages[i] = new Image();    
  25. imgages[i].onload = imgages[i].onerror = _on;    
  26. imgages[i].src = imgList[i];    
  27. }    
  28. /**   
  29. * 如果timeout * total时间范围内,仍有图片未加载出来(判断条件是loaded < total),通知外部环境所有图片均已加载   
  30. * 目的是避免用户等待时间过长   
  31. */    
  32. setTimeout(function () {    
  33. loaded < total && (loaded = total, callback && callback(loaded / total));    
  34. }, timeout * total);    
  35. };    
  36. "function" === typeof define && define.cmd ? define(function () {    
  37. return loader    
  38. }) : window.imgLoader = loader;    
  39. })();   

使用方式(对应代码中的test.html):

XML/HTML Code复制内容到剪贴板
  1. <script src="../js/imgLoader.js"></script>    
  2. <script>    
  3. imgLoader(['../img/page1.jpg', '../img/page2.jpg', '../img/page3.jpg'], function(percentage){    
  4. console.log(percentage)    
  5. });    
  6. </script>   

运行结果:

利用简洁的图片预加载组件提升html5移动页面的用户体验

2. demo说明
本文开篇给出的效果,对应的页面是index.html,关于这个效果还有两个问题需要说明:
1)它用了之前这篇博客Hammer.js+轮播原理实现简洁的滑屏功能介绍的滑屏思路,并把它的一些逻辑包装在了swipe.js,对外提供了一个全局变量Swipe,这个模块有一个init的方法,以便外部通过调用Swipe.init()就能初始化滑屏相关的功能,原来没有提供这个init方法,在js加载完毕就会初始化滑屏功能,有了这个init方法就可以把滑屏的逻辑延迟到加载完毕的时候去初始化。index.html一共引用了5个js:

XML/HTML Code复制内容到剪贴板
  1. <script src="js/zepto.js"></script>    
  2. <script src="js/transition.js"></script>    
  3. <script src="js/hammer.js"></script>    
  4. <script src="js/imgLoader.js"></script>    
  5. <script src="js/swipe.js"></script>   

其中imgLoader.js就是前面介绍图片加载器的实现,前三个js都是为最后一个swipe.js服务的,感兴趣的可以继续我的博客利用轮播原理结合hammer.js实现简洁的滑屏功能了解相关内容。不过滑屏不是本文的重点,不了解swipe.js不会影响理解本文的内容~
2)虽然我在demo中用到了3张比较大的图片,但是由于在本地环境,加载速度还是非常快,所以一开始的时候,很难看到预加载的效果,最后只能想办法在每个进度回调之前做一下延迟,这才可以看到前面gif图片一开始的那个loading效果,实现方式是:

XML/HTML Code复制内容到剪贴板
  1. //模拟加载慢的效果    
  2. var callbacks = [];    
  3. imgLoader(['img/page1.jpg', 'img/page2.jpg', 'img/page3.jpg'], function (percentage) {    
  4. var i = callbacks.length;    
  5. callbacks.push(function(){    
  6. setTimeout(function(){    
  7. var percentT = percentage * 100;    
  8. $('#loader__info').html('Loading ' + (parseInt(percentT)) + '%');    
  9. $('#loader__progress')[0].style.width = percentT + '%';    
  10. if (percentage == 1) {    
  11. setTimeout(function(){    
  12. $('#loader').remove();    
  13. Swipe.init();    
  14. }, 600);    
  15. }    
  16. callbacks[i + 1] && callbacks[i + 1]();    
  17. },600);    
  18. });    
  19. if(percentage == 1) {    
  20. callbacks[0]();    
  21. }    
  22. });   

在真实环境,最好还是不要刻意去加这种延迟,没必要为了让用户看到一个好看有趣的加载效果,就浪费它不必要的等待时间,所以真实环境还是应该用下面的代码:

XML/HTML Code复制内容到剪贴板
  1. imgLoader(['img/page1.jpg', 'img/page2.jpg', 'img/page3.jpg'], function (percentage) {    
  2. var percentT = percentage * 100;    
  3. $('#loader__info').html('Loading ' + (parseInt(percentT)) + '%');    
  4. $('#loader__progress')[0].style.width = percentT + '%';    
  5. if (percentage == 1) {    
  6. $('#loader').remove();    
  7. Swipe.init();    
  8. }    
  9. });   

3. 注意事项
预加载是一种比较常见的实现效果,但是在使用的时候,有些问题需要注意:
1)什么时候用
页面大的时候用,一般页面大小超过3M就该考虑使用;页面内包含数据量比较大的图片,在手机端测试能够明显看到加载缓慢的时候,可以考虑使用。
2)尽量使用sprite图片
3)加载效果实现的时候,尽量不用图片,即使要用也应该用很小的图片,否则加载效果卡在那就没有意义了。
4. 总结
本文主要介绍了一个简单的图片预加载器,可应用于h5移动页面的开发当中,在它的思路之下,如果有必要的话,还可以对它进行一些改造,用它来加载其它类型的资源,比如音频或者视频文件,毕竟这些类型的DOM对象也都有提供类似Image对象的属性和回调。与预加载的方式相反的,还有一种图片懒加载的技术,现在网上已经有比较好用的jquery插件了,不过还是很值的去深入了解下它的思路跟实现要点,等我有时间去研究研究。同时感谢大家一直以来对三水点靠木网站的支持!

HTML / CSS 相关文章推荐
css3新增颜色表示方式分享
Apr 15 HTML / CSS
纯CSS3实现圆角效果(含IE兼容解决方法)
May 07 HTML / CSS
css3 图片圆形显示 如何CSS将正方形图片显示为圆形图片布局
Oct 10 HTML / CSS
CSS3属性box-shadow使用指南
Dec 09 HTML / CSS
CSS3中Transition动画属性用法详解
Jul 04 HTML / CSS
CSS3实现点击放大的动画实例代码
Feb 27 HTML / CSS
html5使用Canvas绘图的使用方法
Nov 21 HTML / CSS
Html5移动端获奖无缝滚动动画实现示例
Jun 25 HTML / CSS
html5 浏览器支持 如何让所有的浏览器都支持HTML5标签样式
Dec 07 HTML / CSS
详解基于canvas的视频遮罩插件
Jan 04 HTML / CSS
html5用video标签流式加载的实现
May 20 HTML / CSS
Html5移动端网页端适配(js+rem)
Feb 03 HTML / CSS
使用HTML5 Canvas绘制直线或折线等线条的方法讲解
Mar 14 #HTML / CSS
借助HTML5 Canvas来绘制三角形和矩形等多边形的方法
Mar 14 #HTML / CSS
HTML5 Canvas绘制文本及图片的基础教程
Mar 14 #HTML / CSS
用HTML5 Canvas API中的clearRect()方法实现橡皮擦功能
Mar 15 #HTML / CSS
网页中的电话号码如何实现一键直呼效果_附示例
Mar 15 #HTML / CSS
HTML5使用Audio标签实现歌词同步的效果
Mar 17 #HTML / CSS
HTML5仿手机微信聊天界面
Mar 18 #HTML / CSS
You might like
ThinkPHP采用实现三级循环代码实例
2014/07/18 PHP
php通过curl添加cookie伪造登陆抓取数据的方法
2016/04/02 PHP
ThinkPHP3.2框架使用addAll()批量插入数据的方法
2017/03/16 PHP
php如何比较两个浮点数是否相等详解
2019/02/12 PHP
神奇的代码 通杀各种网站-可随意修改复制页面内容
2008/07/17 Javascript
Javascript表达式中连续的 &amp;&amp; 和 || 之赋值区别
2010/10/17 Javascript
JavaScript里实用的原生API汇总
2015/05/14 Javascript
JavaScript实现的经典文件树菜单效果
2015/09/08 Javascript
jQuery弹簧插件编写基础之“又见弹窗”
2015/12/11 Javascript
自动适应iframe右边的高度
2016/12/22 Javascript
JS变量及其作用域
2017/03/29 Javascript
jQuery遮罩层实例讲解
2017/05/11 jQuery
Angular限制input框输入金额(是小数的话只保留两位小数点)
2017/07/13 Javascript
微信小程序基于本地缓存实现点赞功能的方法
2017/12/18 Javascript
Node.js成为Web应用开发最佳选择的原因
2018/02/05 Javascript
使用vue-cli打包过程中的步骤以及问题的解决
2018/05/08 Javascript
jQuery实现监听下拉框选中内容发生改变操作示例
2018/07/13 jQuery
浅谈关于iview表单验证的问题
2018/09/29 Javascript
jquery validate 实现动态增加/删除验证规则操作示例
2019/10/28 jQuery
[42:11]TNC vs Pain 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
python获得一个月有多少天的方法
2015/06/04 Python
python导入时小括号大作用
2017/01/10 Python
Python使用MD5加密算法对字符串进行加密操作示例
2018/03/30 Python
Pycharm导入Python包,模块的图文教程
2018/06/13 Python
selenium+python实现自动化登录的方法
2018/09/04 Python
Python使用reportlab模块生成PDF格式的文档
2019/03/11 Python
python判断文件夹内是否存在指定后缀文件的实例
2019/06/10 Python
PyQt5高级界面控件之QTableWidget的具体使用方法
2020/02/23 Python
python数据预处理 :数据共线性处理详解
2020/02/24 Python
波兰快递服务:Globkurier.pl
2019/11/08 全球购物
咖啡书吧创业计划书
2014/01/13 职场文书
停车位租赁协议书
2014/09/24 职场文书
管理人员岗位职责
2015/02/14 职场文书
计生个人工作总结
2015/02/28 职场文书
小学语文教师研修感悟
2015/11/18 职场文书
导游词之天下银坑景区
2019/11/21 职场文书