zoukankan      html  css  js  c++  java
  • 利用简洁的图片预加载组件提升h5移动页面的用户体验--转载

    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服务的,不了解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插件了,不过还是很值的去深入了解下它的思路跟实现要点,等我有时间去研究研究再写博客来介绍,敬请关注!

    记下点滴,等老了慢慢欣赏
  • 相关阅读:
    Redis 数据类型
    Redis 配置
    Redis 安装
    Redis 简介
    MongoDB 自动增长
    MongoDB 固定集合
    IDEA安装
    云市场 > 软件服务 > 建站系统 > 建站模板-官网企业套餐建站模板-官网企业套餐
    腾讯云SSL证书管理
    4-1 创建项目,并了解项目目录下部分文件的作用
  • 原文地址:https://www.cnblogs.com/binmengxue/p/5266461.html
Copyright © 2011-2022 走看看