这里有一小段代码扩展了内置的 jQuery 承诺,适用于图像的 .load() 事件。这允许您执行一些非常简单的编码来等待您的图像组加载。这是 jQuery 的附加代码,用于支持图像加载的承诺:
(function() {
// hook up a dummy animation that completes when the image finishes loading
// If you call this before doing an animation, then animations will
// wait for the image to load before starting
// This does nothing for elements in the collection that are not images
// It can be called multiple times with no additional side effects
var waitingKey = "_waitingForLoad";
var doneEvents = "load._waitForLoad error._waitForLoad abort._waitForLoad";
jQuery.fn.waitForLoad = function() {
return this.each(function() {
var self = $(this);
if (this.tagName && this.tagName.toUpperCase() === "IMG" &&
!this.complete && !self.data(waitingKey)) {
self.queue(function(next) {
// nothing to do here as this is just a sentinel that
// triggers the start of the fx queue
// it will get called immediately and will put the queue "inprogress"
}).on(doneEvents, function() {
// remove flag that we're waiting,
// remove event handlers
// and finish the pseudo animation
self.removeData(waitingKey).off(doneEvents).dequeue();
}).data(waitingKey, true);
}
});
};
// replace existing promise function with one that
// starts a pseudo-animation for any image that is in the process of loading
// so the .promise() method will work for loading images
var oldPromise = jQuery.fn.promise;
jQuery.fn.promise = function() {
this.waitForLoad();
return oldPromise.apply(this, arguments);
};
})();
这通过覆盖当前的.promise() 方法来工作,当为集合中尚未完成加载的每个图像调用.promise() 方法时,它会在jQuery“fx”队列中启动一个虚拟动画,并且那个虚拟当图像的“加载”事件触发时动画完成。因为 jQuery 已经支持动画的 Promise,在为每个尚未加载的图像启动虚拟动画之后,它可以只调用原始的 .promise() 方法,jQuery 会完成创建 Promise 并跟踪动画时间的所有工作完成并在动画全部完成时解决承诺。实际上,我很惊讶 jQuery 自己没有这样做,因为它的附加代码量如此之少,并且利用了他们已经在做的很多事情。
这是此扩展的测试 jsFiddle:http://jsfiddle.net/jfriend00/bAD56/
关于 jQuery 中内置的 .promise() 方法的一个非常好的事情是,如果您在对象集合上调用它,它将返回给您一个主 Promise,该主 Promise 只有在所有单独的 Promise 都已解决后才会解决,所以你不必做所有的家务——它会为你做那些肮脏的工作。
.promise() 的覆盖是否是一个好方法,这是一个见仁见智的问题。对我来说,为现有的.promise() 方法添加一些额外的功能似乎很好,这样除了动画之外,它还可以让您管理图像load 事件。但是,如果该设计选择不是您喜欢的,并且您宁愿保留现有的.promise() 方法,那么可以将图像加载承诺行为放在新方法.loadPromise() 上。要以这种方式使用它,而不是以分配 oldPromise = ... 开头的代码块,您可以替换为:
jQuery.fn.loadPromise = function() {
return this.waitForLoad().promise();
};
而且,要检索包含图像load 事件的承诺事件,您只需调用obj.loadPromise() 而不是obj.promise()。本文中的其余文本和示例假定您使用的是.promise() 覆盖。如果您切换到.loadPromise(),则必须在剩余的文本/演示中替换它。
这个扩展中的概念也可以用于 DOM 中的图像,因为您可以执行类似的操作,这样您就可以在 DOM 中的一组图像加载后立即执行某些操作(无需等待所有图像待加载):
$(document).ready(function() {
$(".thumbnail").promise().done(function() {
// all .thumbnail images are now loaded
});
});
或者,与原始问题无关,但您可以使用此扩展程序做一些有用的事情是您可以等待每个单独的图像加载,然后在加载后立即在每个图像上启动动画:
$(document).ready(function() {
$(".thumbnail").waitForLoad().fadeIn(2000);
});
每张图片都会在加载完成后开始淡入淡出(如果已经加载,则立即淡入淡出)。
而且,使用上述扩展程序的代码如下所示:
var imagesURLArray = [
'http://placekitten.com/200/300',
'http://placekitten.com/100/100',
'http://placekitten.com/400/200'];
// build a jQuery collection that has all your images in it
var images = $();
$.each(imagesURLArray, function(index, value) {
images = images.add($("<img>").attr("src", value));
});
images.promise().done(function() {
// all images are done loading now
images.each(function() {
console.log(this.height + ", " + this.width);
});
});
工作 jsFiddle 演示:http://jsfiddle.net/jfriend00/9Pd32/
或者,在现代浏览器(支持数组上的.reduce())中,您可以使用这个:
imagesURLArray.reduce(function(collection, item) {
return collection.add($("<img>").attr("src", item));
}, $()).promise().done(function() {
// all images are done loading now
images.each(function() {
console.log(this.height + ", " + this.width);
});
});;