网页前端设计

http://www.86y.org

搜索文章

手机端加载资源特效代码及完整实例

用声音读出全文关注我吧
 2016/3/11 15:54:03 阅读次数:5646

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

loader

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>

运行结果:

image

2、demo说明

虽然我在demo中用到了3张比较大的图片,但是由于在本地环境,加载速度还是非常快,所以一开始的时候,很难看到预加载的效果,最后只能想办法在每个进度回调之前做一下延迟,这才可以看到前面gif图片一开始的那个loading效果,实现方式是:

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();
    }
});

3、完整DEMO代码

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no,minimal-ui" name="viewport"/>
<title>手机端加载资源特效代码及完整实例</title>
<style>
*{margin:0;padding:0;}
html,body{height:100%;}
#loader{position:fixed;width:200px;height:30px;top:50%;left:50%;margin:-25px 0 0 -110px;padding:10px;text-align:center;color:green;background:rgba(255,255,255,.7);border-radius:5px;}
#loader_progress{width:0;height:10px;background:green;overflow:hidden;display:block;border-radius:5px;}
</style>
</head>

<body>
<div id="loader">	
	<p id="loader_progress"></p>
	<span id="loader_info"></span>
</div>
<img src="http://desk.fd.zol-img.com.cn/t_s1440x900/g5/M00/04/02/ChMkJlbiLveIFHORAAeKTeFBZKsAANPTAJqrdgAB4pl014.jpg" />
<img src="http://desk.fd.zol-img.com.cn/t_s1440x900/g5/M00/04/02/ChMkJlbiLvOIHq8bAArWap88g8wAANPTAAmNEAACtaC899.jpg" />
<img src="http://desk.fd.zol-img.com.cn/t_s1440x900/g5/M00/04/02/ChMkJlbiLwGIELqSABbb1JLEfAkAANPTgCTCkUAFtvs107.jpg" />
<script>
(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;
})();

imgLoader(['http://desk.fd.zol-img.com.cn/t_s1440x900/g5/M00/04/02/ChMkJlbiLveIFHORAAeKTeFBZKsAANPTAJqrdgAB4pl014.jpg',"http://desk.fd.zol-img.com.cn/t_s1440x900/g5/M00/04/02/ChMkJlbiLvOIHq8bAArWap88g8wAANPTAAmNEAACtaC899.jpg","http://desk.fd.zol-img.com.cn/t_s1440x900/g5/M00/04/02/ChMkJlbiLwGIELqSABbb1JLEfAkAANPTgCTCkUAFtvs107.jpg"], function (percentage) {
    var percentT = percentage * 100;
    document.getElementById('loader_info').innerHTML='Loading ' + (parseInt(percentT)) + '%'
    document.getElementById('loader_progress').style.width = percentT + '%'
    if (percentage == 1) {
    	document.getElementById('loader').parentNode.removeChild(document.getElementById('loader'));
    }
});
</script>
</body>
</html>

4、总结

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


大家有什么问题或技术上的想法可以在此与大家分享,也可以加入前端爱好者QQ群(141999928)一起学习进步:【幸凡前端技术交流群】
0

如果您觉得本文的内容对您的学习有所帮助,捐赠与共勉,支付宝(左)或微信(右)

阅读全文内容关闭