前言

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

imagepool前端图片加载管理器(JavaScript图片连接池)的更多相关文章

  1. Android Fresco (Facebook开源的图片加载管理库)

    Fresco是Facebook开源的一个图片加载和管理库. 这里是Fresco的GitHub网址. 同类型的开源库市面有非常多,比如Picasso, Universal Image Loader, G ...

  2. js判断图片加载完成后获取图片实际宽高

    通常,我们会用jq的.width()/.height()方法获取图片的宽度/高度或者用js的.offsetwidth/.offsetheight方法来获取图片的宽度/高度,但这些方法在我们通过样式设置 ...

  3. 当图片加载失败时更换图片, Firefox onerror 报错

    当图片加载失败时更换图片. <!DOCTYPE html> <meta charset="UTF-8"> <img src="http:// ...

  4. web前端图片加载优化,从图片模糊到清晰的实现过程

    在网页图片显示的时候,会发现许多网站采用了先模糊,然后在慢慢清晰的过程,这样的加载用户体验是比较好的,那么如何实现呐? 默认加载2张图片,一张缩略图,一张原图,当打开网页的时候默认只显示缩略图,然后我 ...

  5. 19_Android中图片处理原理篇,关于人脸识别网站,图片加载到内存,图片缩放,图片翻转倒置,网上撕衣服游戏案例编写

    1加载图片到内存 (1).数码相机照片特别是大于3m以上的,内存吃不消,会报OutOfMemoryError,若是想只显示原图片的1/8,可以通过BitmapFactory.Options来实现,具体 ...

  6. vue中的图片加载与显示默认图片

    HTML: <div class="content-show-img"> <div class="show-img"> <img ...

  7. JS图片加载失败用默认图片代替

    1.onerror 事件会在文档或图像加载过程中发生错误时被触发. 当图片不存在时,将触发onerror,onerror 中img为 指定的默认图片. 图片存在则显示正常图片,图片不存在将显示默认. ...

  8. JS图片加载失败显示默认图片

    代码如下: <div id='photo<%# Container.DataItemIndex+1%>' style="position: absolute; displa ...

  9. vue2.0实现图片加载失败默认显示图片

    <div class="bg"> <img :src="goods.phoneFloorAd.resUrl" :onerror="e ...

随机推荐

  1. 移动适配请使用比rem等更好的布局方案

      移动端大行其道,rem/em.百分比.响应式方案更是层出不穷,看见周围的伙伴们都在对使用rem和百分比情有独钟,可我却偏不爱,之所以出现如此多的方法,其目的只有一个屏幕适配.   屏幕适配顾名思义 ...

  2. 复利计算APP版-----娱乐一下

    先不说那么多,下载地址来一个:http://pan.baidu.com/s/1eSz2GBg 目前版本号为:0.3 lastest 软件上线了!三平台首发! 下载地址: http://shouji.b ...

  3. JS应用,表单上的一些东西

    例: <body> <form>我的生日是哪一年? <input type="text" value="" id="t1 ...

  4. 递推+高精度 UVA 10497 Sweet Child Makes Trouble(可爱的孩子惹麻烦)

    题目链接 题意: n个物品全部乱序排列(都不在原来的位置)的方案数. 思路: dp[i]表示i个物品都乱序排序的方案数,所以状态转移方程.考虑i-1个物品乱序,放入第i个物品一定要和i-1个的其中一个 ...

  5. 【转】浅谈truncate的使用

    delete 操作不会改变表的高水标记,因此如果我们对一个表插入1000万条数据,然后再回滚(对insert操作做回滚相当于相应地做delete操作),会使表的高水标记增长得很高,这时虽然我们操作的表 ...

  6. 图片采用base64压缩,可以以字符串的形式传送base64给服务端转存为图片

    (function () { var coverImage = document.querySelector('<div id="coverImage">file< ...

  7. Android 学习笔记之一 “Unable to establish loopback connection”

    今天碰到一个错误:Unable to establish loopback connection,在网上找各种方法都解决不了,后来看一个帖子说是要关闭系统防火墙,尝试了下还是不行.最后是进任务管理器杀 ...

  8. mac 之 jmeter下载、解压、启动

    1:下载地址:http://jmeter.apache.org/download_jmeter.cgi 2:双击下载的zip文件,即可解压 3:打开终端,cd 到解压的目录下 例如:cd  /User ...

  9. Artifact Project3:war exploded: Error during artifact deployment. See server log for details.

    第一次建Struts2 idea遇到了这个问题,很莫名其妙,搞了几天没解决,几乎要放弃idea了.最后解决的时候也很突然.回想解决的过程,大致如下. 第一种情况:File->Project St ...

  10. Nginx反向代理和负载均衡

    一.Nginx反向代理设置 从80端口转向其他端口反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的 ...