JavaScript для предварительного загрузчика изображений, поддерживающий события

Я пытаюсь найти сценарий предварительного загрузчика изображений.

Хотя я нашел несколько, ни один из них не поддерживает событие, которое запускается после завершения предварительной загрузки.

Кто-нибудь знает какой-нибудь скрипт или плагин jQuery, который это сделает?

Надеюсь, что этот вопрос подходит для stackoverflow - если нет, не стесняйтесь удалить его в одно мгновение.


person SquareCat    schedule 25.11.2011    source источник


Ответы (4)


Вот функция, которая будет предварительно загружать изображения из массива и вызывать ваш обратный вызов, когда последний из них завершится:

function preloadImages(srcs, imgs, callback) {
    var img;
    var remaining = srcs.length;
    for (var i = 0; i < srcs.length; i++) {
        img = new Image();
        img.onload = function() {
            --remaining;
            if (remaining <= 0) {
                callback();
            }
        };
        img.src = srcs[i];
        imgs.push(img);
    }
}

// then to call it, you would use this
var imageSrcs = ["src1", "src2", "src3", "src4"];
var images = [];

preloadImages(imageSrcs, images, myFunction);

И поскольку мы сейчас находимся в возрасте использования промисов для асинхронных операций, вот версия выше, которая использует промисы и уведомляет вызывающую сторону через стандартное промис ES6:

function preloadImages(srcs) {
    function loadImage(src) {
        return new Promise(function(resolve, reject) {
            var img = new Image();
            img.onload = function() {
                resolve(img);
            };
            img.onerror = img.onabort = function() {
                reject(src);
            };
            img.src = src;
        });
    }
    var promises = [];
    for (var i = 0; i < srcs.length; i++) {
        promises.push(loadImage(srcs[i]));
    }
    return Promise.all(promises);
}

preloadImages(["src1", "src2", "src3", "src4"]).then(function(imgs) {
    // all images are loaded now and in the array imgs
}, function(errImg) {
    // at least one image failed to load
});

И вот версия, использующая обещания jQuery 2015 года:

function preloadImages(srcs) {
    function loadImage(src) {
        return new $.Deferred(function(def) {
            var img = new Image();
            img.onload = function() {
                def.resolve(img);
            };
            img.onerror = img.onabort = function() {
                def.reject(src);
            };
            img.src = src;
        }).promise();
    }
    var promises = [];
    for (var i = 0; i < srcs.length; i++) {
        promises.push(loadImage(srcs[i]));
    }
    return $.when.apply($, promises).then(function() {
        // return results as a simple array rather than as separate arguments
        return Array.prototype.slice.call(arguments);
    });
}

preloadImages(["src1", "src2", "src3", "src4"]).then(function(imgs) {
    // all images are loaded now and in the array imgs
}, function(errImg) {
    // at least one image failed to load
});
person jfriend00    schedule 25.11.2011
comment
Для полноты этой функции вы можете добавить обработчики onerror, onabort и тайм-аут, чтобы вы все еще могли вызывать свой обратный вызов, если у одного или нескольких изображений возникли проблемы с загрузкой и они никогда не завершатся успешно. Он будет работать нормально, если изображения не загружаются с ошибками. - person jfriend00; 25.11.2011
comment
Да, это довольно мило. Я пытался это показать, но у меня ничего не вышло. - person Simon; 26.11.2011
comment
Добавлены версии функции preloadImages(), использующие обещания, как стандартные обещания ES6, так и обещания jQuery. - person jfriend00; 09.11.2015
comment
Мне кажется новых дополнений не хватает img.src=src; после var img = новое изображение(); - person Andris Zalitis; 16.12.2015

Для более надежного решения рассмотрите эту функцию PRELOADER с парой обратных вызовов (jsFiddle).

Все просто:

В этом примере я передаю обратные вызовы и хэш изображения внутри литерала Object PRELOADER_OBJECT, а затем переопределяю обратные вызовы внутри PRELOADER:

// preloder object stores image hash
// and event handler callbacks
var PRELOADER_OBJECT = {

    imgArray:"http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg".split(" "),

    progressCallback : function( percent )
    {
        $( '#preloader_progress' ).html( 'preload progress complete : ' + percent + '%' );
        console.log( 'preload progress complete : ', percent );
    },

    completeCallback : function( scope )
    {
        // hide preload indicator, do something when finished
        console.log( 'preload complete!' );
        $( '#preloader_modal' ).delay( 1000 ).animate( { opacity : 0 }, function( )
        {
            $( '.preload_class' ).each( function( index )
            {
                $( this ).delay( index * 100 ).animate( { opacity : 0 } );
            } );
        } );
    }

/*Localize params and create PRELOADER object. 
Needs to loadImages( ); iterate through hash and 
call onPreloadProgress( ) and onPreloadComplete( )
each time until finished. If you're still within
bounds of the image hash, call progressCallback( )
recursively. When finished, fire onCompleteCallback( )*/

var PRELOADER = function( object )
{
    // preloader modal overlay
    this.modal = undefined;

    // progress indicator container
    this.progressIndicator = undefined;

    // image preload progress
    this.progress = undefined;

    // progress callback
    this.progressCallback = undefined;

    // complete callback
    this.completeCallback = undefined;

    // hash to store key : value pairs for image paths
    this.imgArray = undefined; 

    // store images in preloadArray
    this.preloadArray = [];

    // initialize and localize our data
    this.initialize = function( )
    {
        // create preload indicator and overlay modal
        this.createPreloaderModal( );

        // image hash
        this.imgArray = object.imgArray;

        // progress callback event handler
        this.progressCallback = object.progressCallback;

        // complete callback event
        this.completeCallback = object.completeCallback;

        // load images
        this.loadImages( );
    };

    this.progressCallback = function( ) {}; // function to override

    this.completeCallback = function( ) {}; // function to override

    // load images into DOM and fire callbacks
    this.loadImages = function( )
    {
        var that = this;

        // iterate through hash and place images into DOM
        $.each( PRELOADER_OBJECT.imgArray, function( index, object )
        {
            this.image = $( "<img/>", { "src" : object, "class": "preload_class" } ).appendTo( 'body' );

            // mark progress and call progressCallback( ) event handler
            that.progress = Math.ceil( ( index / PRELOADER_OBJECT.imgArray.length ) * 100 );
            that.progressCallback( this.progress );

            that.preloadArray.push( this.image );
        } );

        // check for array bounds and call completeCallback( )
        if ( PRELOADER_OBJECT.imgArray.length )
        {
            this.progressCallback( 100 );
            this.completeCallback( this );
        }
    };

    // create modal to display preload data
    this.createPreloaderModal = function( )
    {
        this.modal = $( '<div/>', { 'id' : 'preloader_modal' } ).appendTo( 'body' );
        this.progressIndicator = $( '<h1/>', { 'id' : 'preloader_progress' } ).appendTo( this.modal );
    };
};

// trigger event chain when DOM loads
$( document ).ready( function( )
{    
    // instantiate PRELOADER instance and pass
    // our JSON data model as a parameter
    var preloader = new PRELOADER( PRELOADER_OBJECT );

    // initialize preloader
    preloader.initialize( );
} );

};​

При загрузке сайта достаточно большой, чтобы потребовать предварительную загрузку изображений, отображение модального текста можно было легко изменить для поддержки анимации jQuery, управляемой данными.

person edwerner    schedule 14.10.2012

Предварительная загрузка и загрузка — это одно и то же. Вы можете вставить изображение (либо создать новое, либо изменить атрибут «src» существующего), но скрыть элемент, используя $("element").hide() или что-то подобное. Перед тем, как сделать это, прикрепите обработчик события загрузки следующим образом:

var images = ["src1", "src2", "src3", "src4"];
var len = images.length;

for(i=0; i<len; i++){
    $("parent element").html('<img id="new-img" style="display:none;" src="'+images[i]+'"/>');
    $("new-img").load(function(){
        //Your image is now "preloaded"

        //Now you can show the image, or do other stuff
        $(this).show();
    });
}
person Simon    schedule 25.11.2011
comment
Спасибо, Саймон. Я пытаюсь настроить это для карты контактов, где есть части страны, которые должны отображаться, каждая из которых является PNG, довольно огромной. Я хочу предварительно загрузить все это, а затем добавить имя класса в его контейнер, чтобы сделать его видимым. Вот и все. Но добавление обратного вызова для запуска при загрузке одного изображения - это не то, что я ищу. - person SquareCat; 25.11.2011
comment
Что вы имеете в виду под предварительной загрузкой? Я предполагаю, что вы хотите загрузить его до того, как он понадобится пользователю, чтобы, когда он / она захочет его увидеть, для его появления не требуется тысячелетие. Используйте приведенный выше код для загрузки изображения. Как только оно будет загружено, вы можете сохранить ссылку на него, чтобы вы могли обращаться к нему, когда пользователю нужно его увидеть. Это эффективно предварительно загружает изображение. - person Simon; 25.11.2011
comment
Спасибо, я понимаю, но это просто не то, что я ищу. Я пытаюсь найти сценарий, который я могу передать массиву источников изображений, который загружает их в фоновом режиме, а затем запускает событие. - person SquareCat; 25.11.2011
comment
Каждый раз, когда изображение загружается, выполняйте var++, и если var == количество изображений для предварительной загрузки, запустите процедуру загрузки всего - person Thilo Savage; 25.11.2011

Предварительная загрузка требует дополнительной работы, такой как создание новых элементов изображения, отслеживание того, все ли они загружены, а затем замена их существующими в DOM. Однако вы можете делать это непосредственно с элементами DOM <img> неограниченное количество раз, не заменяя их.

Мы можем использовать Fetch API для доступа к изображениям, подождать, пока они все будут загружены в promise.all(), а затем за один раз просто заменить атрибуты src элементов img в наиболее подходящее время с помощью window.requestAnimationFrame().

В следующем примере я обновляю атрибуты src элементов img 10 раз. Что касается задержки, я использую время, необходимое для загрузки 4 изображений из API. Итак, как только мы загрузили все изображения, я сразу же размещаю новый запрос, рекурсивно вызывая ту же функцию refreshImagesNTimes.

Конечно, вы можете выбрать загрузку любого количества изображений одновременно и отображать их группами через точные интервалы времени, используя простой механизм setTimeout.

function refreshImagesNTimes(nodeList,count = -1){
  var imgPromises = Array.from({length: nodeList.length})
                         .map(_ => fetch("https://unsplash.it/480/640/?random").then(res => res.blob()));
  Promise.all(imgPromises)
         .then(function(blobs){
                 window.requestAnimationFrame(_ => nodeList.forEach((img, i) => img.src = (window.URL || window.webkitURL).createObjectURL(blobs[i])));
                 --count && refreshImagesNTimes(nodeList, count);
               });
}

var images = document.querySelectorAll("#container img");
refreshImagesNTimes(images,10);
#container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;
  align-items: center;
  margin: auto;
  width: 75vw;
  height: 56.25vw;
  background-color: #000;
  box-sizing: border-box;
}

img {
  width: 45%;
  height: 45%;
  background-color: thistle;
}
<div id="container">
  <img>
  <img>
  <img>
  <img>
</div>

person Redu    schedule 10.09.2017