JavaScript前端图片加载管理器imagepool使用详解_Javascript教程-查字典教程网
JavaScript前端图片加载管理器imagepool使用详解
JavaScript前端图片加载管理器imagepool使用详解
发布时间:2016-12-30 来源:查字典编辑
摘要:前言imagepool是一款管理图片加载的JS工具,通过imagepool可以控制图片并发加载个数。对于图片加载,最原始的方式就是直接写个i...

前言

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前端图片加载管理器的使用方法示例,小伙伴们学会使用了吗?

相关阅读
推荐文章
猜你喜欢
附近的人在看
推荐阅读
拓展阅读
  • 大家都在看
  • 小编推荐
  • 猜你喜欢
  • 最新Javascript教程学习
    热门Javascript教程学习
    编程开发子分类