Вот небольшой фрагмент кода, который расширяет встроенные обещания 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()
, а jQuery выполнит всю работу по созданию обещания и отслеживанию завершения анимации. и разрешение обещания, когда все анимации будут сделаны. Я на самом деле удивлен, что jQuery не делает этого сама, потому что это такой небольшой дополнительный код, который использует многие вещи, которые они уже делают.
Вот тестовый jsFiddle для этого расширения: http://jsfiddle.net/jfriend00/bAD56/
Одна очень приятная особенность встроенного метода .promise()
в jQuery заключается в том, что если вы вызовете его для набора объектов, он вернет вам главный промис, который разрешается только тогда, когда все отдельные промисы были разрешены, поэтому у вас нет делать всю эту уборку - она сделает эту грязную работу за вас.
Это вопрос мнения, является ли переопределение .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);
});
});;
person
jfriend00
schedule
16.07.2014
load
ненадежен для кросс-браузерных изображений - person charlietfl   schedule 15.07.2014resolve
использовать deferred только один раз, поэтому для каждого изображения вам потребуется отдельный deferred. Вы можете объединить их с$.when
, если хотите. - person Bergi   schedule 16.07.2014