JavaScript前端图片加载管理器imagepool使用详解

前言

imagepool是一款管理图片加载的JS工具,通过imagepool可以控制图片并发加载个数。

对于图片加载,最原始的方式就是直接写个img标签,比如:<img src="图片url" />。

经过不断优化,出现了图片延迟加载方案,这回图片的URL不直接写在src属性中,而是写在某个属性中,比如:<img src="" data-src="图片url" />。这样浏览器就不会自动加载图片,等到一个恰当的时机需要加载了,则用js把data-src属性中的url放到img标签的src属性中,或者读出url后,用js去加载图片,加载完成后再设置src属性,显示出图片。

这看起来已经控制的很好了,但依然会有问题。

虽然能做到只加载一部分图片,但这一部分图片,仍然可能是一个比较大的数量级。

这对于PC端来说,没什么大不了,但对于移动端,图片并发加载数量过多,极有可能引起应用崩溃。

因此我们迫切需要一种图片缓冲机制,来控制图片加载并发。类似于后端的数据库连接池,既不会创建过多连接,又能充分复用每一个连接。

至此,imagepool诞生了。

 拙劣的原理图

使用说明

首先要初始化连接池:

var imagepool = initImagePool(5);
     initImagePool 是全局方法,任何地方都可以直接使用。作用是创建一个连接池,并且可以指定连接池的最大连接数,可选,默认为5。

在同一个页面中,多次调用initImagePool均返回同一个核心实例,永远是第一个,有点单例的感觉。比如:

代码如下:

var imagepool1 = initImagePool(3);
 var imagepool2 = initImagePool(7);

此时imagepool1和imagepool2的最大连接数均为3,内部使用的是同一个核心实例。注意,是内部的核心相同,并不是说imagepool1 === imagepool2。

初始化之后,就可以放心大胆的加载图片了。

最简单的调用方法如下:

代码如下:

var imagepool = initImagePool(10);
 imagepool.load("图片url",{
     success: function(src){
         console.log("success:::::"+src);
     },
     error: function(src){
         console.log("error:::::"+src);
     }
 });

直接在实例上调用load方法即可。

load方法有两个参数。第一个参数是需要加载的图片url,第二个参数是各种选项,包含了成功、失败的回调,回调时会传入图片url。

这样写只能传入一张图片,因此,也可以写成如下形式:

代码如下:

var imagepool = initImagePool(10);
 imagepool.load(["图片1url","图片2url"],{
     success: function(src){
         console.log("success:::::"+src);
     },
     error: function(src){
         console.log("error:::::"+src);
     }
 });

通过传入一个图片url数组,就可以传入多个图片了。

每一个图片加载成功(或失败),都会调用success(或error)方法,并且传入对应的图片url。

但有时候我们并不需要这样频繁的回调,传入一个图片url数组,当这个数组中所有的图片都处理完成后,再回调就可以了。

只需加一个选项即可:

代码如下:

var imagepool = initImagePool(10);
 imagepool.load(["图片1url ","图片2url "],{
     success: function(sArray, eArray, count){
         console.log("sArray:::::"+sArray);
         console.log("eArray:::::"+eArray);
         console.log("count:::::"+count);
     },
     error: function(src){
         console.log("error:::::"+src);
     },
     once: true
 });

通过在选项中加一个once属性,并设置为true,即可实现只回调一次。

这一次回调,必然回调success方法,此时error方法是被忽略的。

此时回调success方法,不再是传入一个图片url参数,而是传入三个参数,分别为:成功的url数组、失败的url数组、总共处理的图片个数。

此外,还有一个方法可以获取连接池内部状态:

代码如下:

var imagepool = initImagePool(10);
 console.log(imagepool.info());

通过调用info方法,可以得到当前时刻连接池内部状态,数据结构如下:

Object.task.count 连接池中等待处理的任务数量
     Object.thread.count 连接池最大连接数
     Object.thread.free 连接池空闲连接数
 
     建议不要频繁调用此方法。

最后需要说明的是,如果图片加载失败,最多会尝试3次,如果最后还是加载失败,才回调error方法。尝试次数可在源码中修改。

最最后再强调一下,读者可以尽情的往连接池中push图片,完全不必担心并发过多的问题,imagepool会有条不絮的帮你加载这些图片。

最最最后,必须说明的是,imagepool理论上不会降低图片加载速度,只不过是平缓的加载。

源码

代码如下:

(function(exports){
    //单例
    var instance = null;
    var emptyFn = function(){};
    //初始默认配置
    var config_default = {
        //线程池"线程"数量
        thread: 5,
        //图片加载失败重试次数
        //重试2次,加上原有的一次,总共是3次
        "try": 2
    };
    //工具
    var _helpers = {
        //设置dom属性
        setAttr: (function(){
            var img = new Image();
            //判断浏览器是否支持HTML5 dataset
            if(img.dataset){
                return function(dom, name, value){
                    dom.dataset[name] = value;
                    return value;
                };
            }else{
                return function(dom, name, value){
                    dom.setAttribute("data-"+name, value);
                    return value;
                };
            }
        }()),
        //获取dom属性
        getAttr: (function(){
            var img = new Image();
            //判断浏览器是否支持HTML5 dataset
            if(img.dataset){
                return function(dom, name){
                    return dom.dataset[name];
                };
            }else{
                return function(dom, name){
                    return dom.getAttribute("data-"+name);
                };
            }
        }())
    };
    /**
     * 构造方法
     * @param max 最大连接数。数值。
     */
    function ImagePool(max){
        //最大并发数量
        this.max = max || config_default.thread;
        this.linkHead = null;
        this.linkNode = null;
        //加载池
        //[{img: dom,free: true, node: node}]
        //node
        //{src: "", options: {success: "fn",error: "fn", once: true}, try: 0}
        this.pool = [];
    }
    /**
     * 初始化
     */
    ImagePool.prototype.initPool = function(){
        var i,img,obj,_s;
        _s = this;
        for(i = 0;i < this.max; i++){
            obj = {};
            img = new Image();
            _helpers.setAttr(img, "id", i);
            img.onload = function(){
                var id,src;
                //回调
                //_s.getNode(this).options.success.call(null, this.src);
                _s.notice(_s.getNode(this), "success", this.src);
                //处理任务
                _s.executeLink(this);
            };
            img.onerror = function(e){
                var node = _s.getNode(this);
                //判断尝试次数
                if(node.try < config_default.try){
                    node.try = node.try + 1;
                    //再次追加到任务链表末尾
                    _s.appendNode(_s.createNode(node.src, node.options, node.notice, node.group, node.try));
                }else{
                    //error回调
                    //node.options.error.call(null, this.src);
                    _s.notice(node, "error", this.src);
                }
                //处理任务
                _s.executeLink(this);
            };
            obj.img = img;
            obj.free = true;
            this.pool.push(obj);
        }
    };
    /**
     * 回调封装
     * @param node 节点。对象。
     * @param status 状态。字符串。可选值:success(成功)|error(失败)
     * @param src 图片路径。字符串。
     */
    ImagePool.prototype.notice = function(node, status, src){
        node.notice(status, src);
    };
    /**
     * 处理链表任务
     * @param dom 图像dom对象。对象。
     */
    ImagePool.prototype.executeLink = function(dom){
        //判断链表是否存在节点
        if(this.linkHead){
            //加载下一个图片
            this.setSrc(dom, this.linkHead);
            //去除链表头
            this.shiftNode();
        }else{
            //设置自身状态为空闲
            this.status(dom, true);
        }
    };
    /**
     * 获取空闲"线程"
     */
    ImagePool.prototype.getFree = function(){
        var length,i;
        for(i = 0, length = this.pool.length; i < length; i++){
            if(this.pool[i].free){
                return this.pool[i];
            }
        }
        return null;
    };
    /**
     * 封装src属性设置
     * 因为改变src属性相当于加载图片,所以把操作封装起来
     * @param dom 图像dom对象。对象。
     * @param node 节点。对象。
     */
    ImagePool.prototype.setSrc = function(dom, node){
        //设置池中的"线程"为非空闲状态
        this.status(dom, false);
        //关联节点
        this.setNode(dom, node);
        //加载图片
        dom.src = node.src;
    };
    /**
     * 更新池中的"线程"状态
     * @param dom 图像dom对象。对象。
     * @param status 状态。布尔。可选值:true(空闲)|false(非空闲)
     */
    ImagePool.prototype.status = function(dom, status){
        var id = _helpers.getAttr(dom, "id");
        this.pool[id].free = status;
        //空闲状态,清除关联的节点
        if(status){
            this.pool[id].node = null;
        }
    };
    /**
     * 更新池中的"线程"的关联节点
     * @param dom 图像dom对象。对象。
     * @param node 节点。对象。
     */
    ImagePool.prototype.setNode = function(dom, node){
        var id = _helpers.getAttr(dom, "id");
        this.pool[id].node = node;
        return this.pool[id].node === node;
    };
    /**
     * 获取池中的"线程"的关联节点
     * @param dom 图像dom对象。对象。
     */
    ImagePool.prototype.getNode = function(dom){
        var id = _helpers.getAttr(dom, "id");
        return this.pool[id].node;
    };
    /**
     * 对外接口,加载图片
     * @param src 可以是src字符串,也可以是src字符串数组。
     * @param options 用户自定义参数。包含:success回调、error回调、once标识。
     */
    ImagePool.prototype.load = function(src, options){
        var srcs = [],
            free = null,
            length = 0,
            i = 0,
            //只初始化一次回调策略
            notice = (function(){
                if(options.once){
                    return function(status, src){
                        var g = this.group,
                            o = this.options;
                        //记录
                        g[status].push(src);
                        //判断改组是否全部处理完成
                        if(g.success.length + g.error.length === g.count){
                            //异步
                            //实际上是作为另一个任务单独执行,防止回调函数执行时间过长影响图片加载速度
                            setTimeout(function(){
                                o.success.call(null, g.success, g.error, g.count);
                            },1);
                        }
                    };
                }else{
                    return function(status, src){
                        var o = this.options;
                        //直接回调
                        setTimeout(function(){
                            o[status].call(null, src);
                        },1);
                    };
                }
            }()),
            group = {
                count: 0,
                success: [],
                error: []
            },
            node = null;
        options = options || {};
        options.success = options.success || emptyFn;
        options.error = options.error || emptyFn;
        srcs = srcs.concat(src);
        //设置组元素个数
        group.count = srcs.length;
        //遍历需要加载的图片
        for(i = 0, length = srcs.length; i < length; i++){
            //创建节点
            node = this.createNode(srcs[i], options, notice, group);
            //判断线程池是否有空闲
            free = this.getFree();
            if(free){
                //有空闲,则立即加载图片
                this.setSrc(free.img, node);
            }else{
                //没有空闲,将任务添加到链表
                this.appendNode(node);
            }
        }
    };
    /**
     * 获取内部状态信息
     * @returns {{}}
     */
    ImagePool.prototype.info = function(){
        var info = {},
            length = 0,
            i = 0,
            node = null;
        //线程
        info.thread = {};
        //线程总数量
        info.thread.count = this.pool.length;
        //空闲线程数量
        info.thread.free = 0;
        //任务
        info.task = {};
        //待处理任务数量
        info.task.count = 0;
        //获取空闲"线程"数量
        for(i = 0, length = this.pool.length; i < length; i++){
            if(this.pool[i].free){
                info.thread.free = info.thread.free + 1;
            }
        }
        //获取任务数量(任务链长度)
        node = this.linkHead;
        if(node){
            info.task.count = info.task.count + 1;
            while(node.next){
                info.task.count = info.task.count + 1;
                node = node.next;
            }
        }
        return info;
    };
    /**
     * 创建节点
     * @param src 图片路径。字符串。
     * @param options 用户自定义参数。包含:success回调、error回调、once标识。
     * @param notice 回调策略。 函数。
     * @param group 组信息。对象。{count: 0, success: [], error: []}
     * @param tr 出错重试次数。数值。默认为0。
     * @returns {{}}
     */
    ImagePool.prototype.createNode = function(src, options, notice, group, tr){
        var node = {};
        node.src = src;
        node.options = options;
        node.notice = notice;
        node.group = group;
        node.try = tr || 0;
        return node;
    };
    /**
     * 向任务链表末尾追加节点
     * @param node 节点。对象。
     */
    ImagePool.prototype.appendNode = function(node){
        //判断链表是否为空
        if(!this.linkHead){
            this.linkHead = node;
            this.linkNode = node;
        }else{
            this.linkNode.next = node;
            this.linkNode = node;
        }
    };
    /**
     * 删除链表头
     */
    ImagePool.prototype.shiftNode = function(){
        //判断链表是否存在节点
        if(this.linkHead){
            //修改链表头
            this.linkHead = this.linkHead.next || null;
        }
    };
    /**
     * 导出对外接口
     * @param max 最大连接数。数值。
     * @returns {{load: Function, info: Function}}
     */
    exports.initImagePool = function(max){
        if(!instance){
            instance = new ImagePool(max);
            instance.initPool();
        }
        return {
            /**
             * 加载图片
             */
            load: function(){
                instance.load.apply(instance, arguments);
            },
            /**
             * 内部信息
             * @returns {*|any|void}
             */
            info: function(){
                return instance.info.call(instance);
            }
        };
    };
}(this));

以上就是这款特别棒的javascript前端图片加载管理器的使用方法示例,小伙伴们学会使用了吗?

(0)

相关推荐

  • js判断图片加载完成后获取图片实际宽高的方法

    本文实例讲述了js判断图片加载完成后获取图片实际宽高的方法.分享给大家供大家参考,具体如下: 通常,我们会用jq的.width()/.height()方法获取图片的宽度/高度或者用js的.offsetwidth/.offsetheight方法来获取图片的宽度/高度,但这些方法在我们通过样式设置了图片的宽高后获取的就不是图片的实际宽高,这显然在有些时候不是我们想要的结果,那么有没有一种方法来获取这样的实际宽高呢?答案是有的.下面的代码就能解决这样的问题: <img src="01.jpg&q

  • JavaScript控制图片加载完成后调用回调函数的方法

    本文实例讲述了JavaScript控制图片加载完成后调用回调函数的方法.分享给大家供大家参考.具体分析如下: 这段代码可以控制指定区域内的图片加载完成后执行指定的回调函数. 复制代码 代码如下: function when_images_loaded($img_container, callback) { /* do callback when images in $img_container (jQuery object) are loaded. Only works when ALL ima

  • JavaScript 计算图片加载数量的代码

    通过JavaScript 来计算当前图片加载的张数. 原理: 先定义一个图片的数组,然后通过image的onload事件来计算,注意,onload在ie和火狐有所不同. 最后需要一个进度条与之结合即可. 这个脚本在做游戏的地方用得比较多. 演示: 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1

  • Android程序开发ListView+Json+异步网络图片加载+滚动翻页的例子(图片能缓存,图片不错乱)

    例子中用于解析Json的Gson请自己Google下载 主Activity: package COM.Example.Main; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import COM.Example.Main.R; import COM.Example.Main.stringG

  • js实现图片加载时候逐渐出现的杂色效果

    虽然本效果与马赛克有些差别,但可从思路上为你提供一份参考. 图片逐渐出现杂色效果 image1.filters.item(0).apply() image1.filters.item(0).transition = 12 image1.Style.visibility = "" image1.filters(0).play(2.0) [Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]

  • 解决js图片加载时出现404的问题

    运营网站久了之后,无法避免会出现图片404的情况,原因可能是图片文件本来就不存在或目前不存在.常见的解决方案是将404图片隐藏或者是替换为默认的图片.  img标签事件属性 img标签可使用的时间属性有: onabort, onbeforeunload, onblur, onchange, onclick, oncontextmenu, ondblclick, ondrag, ondragend, ondragenter, ondragleave, ondragover, ondragstart

  • JavaScript实现页面滚动图片加载(仿lazyload效果)

    为什么写这篇文章? 1.优化页面很实用的方法,技术实现不难: 2.搜索了相关内容的文章,好像都是用jQuery的方法,可是如果不用jQuery的站长难道就不能用这种方法了么: 3.做技术分享也是在让更多人帮自己测试,因为这个本人木有在项目中实际用到,都是自己琢磨的,所有如果有问题请大家指出,先谢谢了: 4.这个月的博客还没写: 5.刚好木有工作任务,此时不写更待何时... 现在的页面大多都具有的特点 - 内容丰富,图片较多:像我们经常浏览的淘宝,京东,团购网站之类的(本人网购控,属于一个月不在网

  • Javascript实现图片加载从模糊到清晰显示的方法

    1.背景介绍 在网络相册应用中用户查看照片是最朴素的需求,当网络比较慢的时候查看照片等待的时间是比较长的,用户体验会很差. 2.现状 现在加载照片的方法主要有一下两种: (1)最原始的方式在html页面直接用img标签加载显示照片.该方法在网络速度比较慢或者要显示的照片比较大的时候会页面出现空白的等待过程,并且不能开始给用户看到照片大概的情况,用户体验比较不好 (2)在html页面先用img标签加载显示照片的缩略图,同时用javascript隐藏的加载照片的原图,等照片大图加载完成后再将原图显示

  • JavaScript前端图片加载管理器imagepool使用详解

    前言 imagepool是一款管理图片加载的JS工具,通过imagepool可以控制图片并发加载个数. 对于图片加载,最原始的方式就是直接写个img标签,比如:<img src="图片url" />. 经过不断优化,出现了图片延迟加载方案,这回图片的URL不直接写在src属性中,而是写在某个属性中,比如:<img src="" data-src="图片url" />.这样浏览器就不会自动加载图片,等到一个恰当的时机需要加载

  • JavaScript无阻塞加载和defer、async详解

    无阻塞加载 把js放在head里,浏览器是怎么去执行它的呢,是按顺序加载还是并行加载呢?在旧的浏览器下,都是按照先后顺序来加载的,这就保证了加载的js依赖不会发生问题.但是少部分新的浏览器已经开始允许并行加载js了,也就是说可以同时下载js文件,但是还是按先后顺序执行文件的. 下载是异步的没问题,但是每个javascript执行的时候还是同步的,就是先出现的script标签一定是先执行,即使是并行下载它是最后一个下载完成的,除非标有defer的script标签.任何javascript在执行的时

  • js前端图片加载异常兜底方案

    目录 背景 <img>加载错误解决方案 内联事件 全局img添加事件 利用error事件捕获 替换src方式的最优解 CSS处理的最优解 <img>加载超时解决方案 嗅探切换Domain(CNAME) 服务端下发Domain(CNAME) background-image加载异常解决方案 自定义事件 嗅探加载情况 添加事件捕获 背景 网络环境总是多样且复杂的,一张图片可能会因为网路状况差而加载失败或加载超长时间,也可能因为权限不足或者资源不存在而加载失败,这些都会导致用户体验变差,

  • Three.js加载外部模型的教程详解

    1.  首先我们要在官网: https://threejs.org/ 下载我们three.js压缩包,并将其中的build文件夹下的three.js通过script标签对的src属性导入到我们的页面中 2.  创建three.js核心对象 Scene(场景) Camera(相机) Light(光源) Mesh(模型) Renderer(渲染器) 最后一步就是渲染显示在我们的页面上了renderer.render(scene,camera) 3.  OBJ模型的导入 <script type=&quo

  • BootstrapTable加载按钮功能实例代码详解

    1      html <!--工具栏--> <div id="toolbar" class="btn-group"> <div style="float:left;margin-right: 10px"> <button class="btn btn-danger"onclick="openModal('add',0,'')">增加</button&g

  • PyTorch加载自己的数据集实例详解

    数据预处理在解决深度学习问题的过程中,往往需要花费大量的时间和精力. 数据处理的质量对训练神经网络来说十分重要,良好的数据处理不仅会加速模型训练, 更会提高模型性能.为解决这一问题,PyTorch提供了几个高效便捷的工具, 以便使用者进行数据处理或增强等操作,同时可通过并行化加速数据加载. 数据集存放大致有以下两种方式: (1)所有数据集放在一个目录下,文件名上附有标签名,数据集存放格式如下: root/cat_dog/cat.01.jpg root/cat_dog/cat.02.jpg ...

  • JS 加载性能Tree Shaking优化详解

    目录 正文 什么是 Tree Shaking 寻找 Tree Shaking 的机会 防止 Babel 将 ES6 模块转换为 CommonJS 模块 留意 side effects 只导入你需要的 更复杂的情况 总结 正文 随着 web 应用复杂性增加,JS 代码文件的大小也在不断的攀升,截住 2021年9月,在 httparchive 上有统计显示——在移动设备上 JS 传输大小大约为 447 KB,桌面端 JS 传输大小大约为 495 KB,注意这仅仅是在网络中传输的 JS 文件大小,JS

随机推荐