Image preloader javascript that supports events

人盡茶涼 提交于 2019-11-25 22:46:45

Here's a function that will preload images from an array and call your callback when the last one has finished:

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

And since we're now in the age of using promises for asynchronous operations, here's a version of the above that uses promises and notifies the caller via an ES6 standard promise:

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

And, here's a version using 2015 jQuery promises:

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

For a more robust solution, consider this PRELOADER function with a couple of callbacks (jsFiddle).

Keeping it simple:

In this example, I'm passing callbacks and an image hash inside an Object literal PRELOADER_OBJECT, then overriding the callbacks inside 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( );
} );

};​

With a site load large enough to require an image preloader, the modal text display could be easily modified to support a data-driven jQuery animation.

Preloading and loading are the same thing. You can insert the image (either create a new one or change the "src" attribute of an existing one) but hide the element using $("element").hide() or something similar. Before you do this, attach a load event handler like so:

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

Preloading takes some extra work like creating new image elements, monitoring if they are all loaded and then replacing them with the existing ones in the DOM. However you may do this directly on the DOM <img> elements indefinitely many times without replacing them.

We may use the Fetch API to access the images, wait until they are all downloaded within a promise.all() and then in one go just replace the src attributes of the img elements at the most suitable time by using window.requestAnimationFrame().

In the following example I refresh the src attributes of the img elements 10 times. As per the delay, i am using the the time it takes up to load 4 images from an API. So once we have all images loaded i am immediately placing a new request by calling the same refreshImagesNTimes function recursively.

You may of course chose to load as many image blobs as you like all at once and display them in groups in precise time intervals by using a simple setTimeout mechanism.

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>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!