原文地址:http://www.grycheng.com/?p=2188

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

1. 实现思路

html里面的img标签和css中background-imag等都会触发浏览器去加载相关的图片,但是如果这个图片已经加载过了的话,浏览器就会直接使用这张已经加载好的图片,从而能够瞬间在页面中渲染出来。通过javascript,创建Image对象,然后把这些对象的src属性设置成要加载的图片地址也能触发浏览器加载图片,利用这一点就能实现图片预加载的功能:在页面里首先把那些用到了相关的图片的元素给藏掉,然后用js去加载图片,等到所有图片加载完毕再把藏掉的元素显示即可。不过这仅仅是一个基本的实现思路,要完成一个功能较健壮的预加载组件,还有以下三个问题:

1)进度问题

由于预加载的同时,还得做一个预加载的效果,这就需要把加载的进度实时通知到外部上下文才行。关于进度有两个实现方式,第一是已加载的数据大小/总的数据大小,第二是已加载的文件数/总的文件数,在浏览器里面,采用第一种方式是不现实的,根本没有原生的办法可以做到,所以只能采用第二种。

2)图片加载失败的问题

比如说有4张图片,已经加载了50%,在加载第三张的时候出错了,该不该将进度反馈成75%呢?答案是:应该。如果不这么处理的话,进度永远无法到100%,页面主内容就没机会显示了,虽然图片加载有失败的情况,但是跟加载器没有关系,也许图片本身就不存在呢?也就是说图片加载失败不应该影响加载器的功能。

3)图片加载超时的问题

图片不能加载太久,否则用户一直停留在加载效果上看不到主内容,用户的等待时间不可控制地延长,导致用户体验下降,这样就有悖加载器的初衷了。所以应该给每个图片设置一个加载的超时时间,如果在所有图片的超时时间之后,还没加载完,就应该主动放弃加载,通知外部上下文加载完毕,显示主内容。

综合以上这些需求,本文提供的实现是:

(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>

运行结果:

2. demo说明

本文开篇给出的效果,对应的页面是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();
}
});

3. 注意事项

预加载是一种比较常见的实现效果,但是在使用的时候,有些问题需要注意:

1)什么时候用

页面大的时候用,一般页面大小超过3M就该考虑使用;页面内包含数据量比较大的图片,在手机端测试能够明显看到加载缓慢的时候,可以考虑使用。

2)尽量使用sprite图片

3)加载效果实现的时候,尽量不用图片,即使要用也应该用很小的图片,否则加载效果卡在那就没有意义了。

4. 总结

本文主要介绍了一个简单的图片预加载器,可应用于h5移动页面的开发当中,在它的思路之下,如果有必要的话,还可以对它进行一些改造,用它来加载其它类型的资源,比如音频或者视频文件,毕竟这些类型的DOM对象也都有提供类似Image对象的属性和回调。与预加载的方式相反的,还有一种图片懒加载的技术,现在网上已经有比较好用的jquery插件了,不过还是很值的去深入了解下它的思路跟实现要点,等我有时间去研究研究再写博客来介绍,敬请关注!

运用预加载提升H5移动页面的用户体验的更多相关文章

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

    在做h5移动页面,相信大家一定碰到过页面已经打开,但是里面的图片还未加载出来的情况,这种问题虽然不影响页面的功能,但是不利于用户体验.抛开网速的原因,解决这个问题有多方面的思路:最基本的,要从http ...

  2. 移动端-H5预加载页面

    利用简洁的图片预加载组件提升h5移动页面的用户体验   阅读目录 1. 实现思路 2. demo说明 3. 注意事项 4. 总结 在 做h5移动页面,相信大家一定碰到过页面已经打开,但是里面的图片还未 ...

  3. HTML5的页面资源预加载技术(Link prefetch)加速页面加载

    不管是浏览器的开发者还是普通web应用的开发者,他们都在做一个共同的努力:让Web浏览有更快的速度感觉.有很多已知的技术都可以让你的网站速度变得更快:使用CSS sprites,使用图片优化工具,使用 ...

  4. 使用HTML5的页面资源预加载(Link prefetch)功能加速你的页面加载速度

    不管是浏览器的开发者还是普通web应用的开发者,他们都在做一个共同的努力:让Web浏览有更快的速度感觉.有很多已知的技术都可以让你的网站速度变得更快:使用CSS sprites,使用图片优化工具,使用 ...

  5. Flying Pages:在单击之前预加载页面,打开网页快得飞起

    Flying Pages能够实现:在用户点击网页的链接之前,就预加载这个网页,当再点击这个网页时,页面便能飞速打开,能为网站优化加分.接下来,就由LOYSEO来讲解Flying Pages的使用方法. ...

  6. mui webview 预加载

    所谓的预加载技术就是在用户尚未触发页面跳转时,提前创建目标页面,这样当用户跳转时,就可以立即进行页面切换,节省创建新页面的时间,提升app使用体验.mui提供两种方式实现页面预加载. 方式一:通过mu ...

  7. web资源预加载-生产环境实践

    此文记录资源预加载在我们项目的实践,技术难度不算高,重在介绍一套技术方案的诞生与实施,其中都进行了哪些思考,依据什么来做决策,如何进行效果评估,等等.为读者在制定技术方案时提供一定启示. 背景 资源预 ...

  8. Javascript图片预加载详解

    预加载图片是提高用户体验的一个很好方法.图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度.这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速.无缝地发布 ...

  9. 利用CSS、JavaScript及Ajax实现图片预加载的三大方法

    预加载图片是提高用户体验的一个很好方法.图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度.这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速.无缝地发布 ...

随机推荐

  1. switch语法中break,default作用说明

    转自:http://cjhbest999.iteye.com/blog/1137124 关于java中switch使用的一些说明 switch(表达式) { case 常量表达式1:语句1; .... ...

  2. runnable:在线IDE+代码片段分享

    在我之前的博客20个最好的在线IDE中列举过很多在线IDE,可以很方便的在云端执行代码,这样在你手头没有编译器时想试个小程序会非常有用. 今天介绍的这个网站runnable把在线IDE和代码片段结合了 ...

  3. JSP中嵌入java代码的标签方式(转)

    (1)声明变量或方法 :  <%! 声明; %> :慎重使用,因为此方法定义的是全局变量 (2)java片段(scriptlet):  <% java代码; %> (3)表达式 ...

  4. TCP 连接的建立和终止

    三路握手 建立一个TCP连接时会发生下述情形. (1)服务器必须准备好接受外来的连接.这通常通过调用socket.bind和listen这3个函数来完成的,我们称之为被动打开. (2)客户通过调用co ...

  5. vxworks

    VxWorks 是美国 Wind River System 公司( 以下简称风河公司 ,即 WRS 公司)推出的一个实时操作系统.Tornado 是WRS 公司推出的一套实时操作系统开发环境,类似Mi ...

  6. HDU2084JAVA

    数塔 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submissi ...

  7. java 字节流与字符流的区别 (转)

    字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢? 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操 ...

  8. 实现RecycleView动态使列表item可以点击或不可点击切换

    效果 这里讲的是第二个button跳转的Activity,这里和上一篇不同之处在于可以item点击.item子控件点击 继承BaseAdapter 同样也要继承BaseAdapter public c ...

  9. wget下载网站整个目录

    wget -r -p -np -k -P ./data/ http://example.com/eg/ 具体参数: -P 表示下载到哪个目录 -r 表示递归下载 -np 表示不下载旁站连接 -k 表示 ...

  10. mysql case when then else end 用法

    select case when 判断条件 then 输出结果  else 输出结果 end from table