Skip to content

Instantly share code, notes, and snippets.

@jaredrethman
Last active August 29, 2015 14:09
Show Gist options
  • Save jaredrethman/625a8159f6243dbcba76 to your computer and use it in GitHub Desktop.
Save jaredrethman/625a8159f6243dbcba76 to your computer and use it in GitHub Desktop.
// BASED on this article - http://www.bennadel.com/blog/2597-preloading-images-in-angularjs-with-promises.htm
// Preloading Images In AngularJS With Promises.
// I provide a utility class for preloading image objects.
TRMFullScreenApp.factory(
"preloader",
function( $q, $rootScope, $timeout ) {
// I manage the preloading of image objects. Accepts an array of image URLs.
function Preloader( imageLocations ) {
// I am the image SRC values to preload.
this.imageLocations = imageLocations;
this.failedImages = []; //Doesn't get used, but nice to be able to track failed images.
this.cleanedImages = [];
// As the images load, we'll need to keep track of the load/error
// counts when announing the progress on the loading.
this.imageCount = this.imageLocations.length;
this.imageHandleCount = 0;
// I am the possible states that the preloader can be in.
this.states = {
PENDING: 1,
LOADING: 2,
COMPLETED: 3
};
// I keep track of the current state of the preloader.
this.state = this.states.PENDING;
// When loading the images, a promise will be returned to indicate
// when the loading has completed (and / or progressed).
this.deferred = $q.defer();
this.promise = this.deferred.promise;
}
// ---
// STATIC METHODS.
// ---
// I reload the given images [Array] and return a promise. The promise
// will be resolved with the array of image locations.
Preloader.preloadImages = function( imageLocations ) {
var preloader = new Preloader( imageLocations );
return( preloader.load() );
};
// ---
// INSTANCE METHODS.
// ---
Preloader.prototype = {
// Best practice for "instnceof" operator.
constructor: Preloader,
// ---
// PUBLIC METHODS.
// ---
// I determine if the preloader has started loading images yet.
isInitiated: function isInitiated() {
return( this.state !== this.states.PENDING );
},
// I determine if the preloader has successfully loaded all of the images.
isCompleted: function isComplete() {
return( this.state === this.states.COMPLETED );
},
// I initiate the preload of the images. Returns a promise.
load: function load() {
// If the images are already loading, return the existing promise.
if ( this.isInitiated() ) {
return( this.promise );
}
this.state = this.states.LOADING;
for ( var i = 0 ; i < this.imageCount ; i++ ) {
this.loadImageLocation( this.imageLocations[ i ] );
}
// Return the deferred promise for the load event.
return( this.promise );
},
// ---
// PRIVATE METHODS.
// ---
// This is the only image handler. Each image coming in is either flagged with a true or false parameter.
handleImage: function handleImage( imageLocation, exists ) {
this.imageHandleCount++;
//Track failed image locations.
if(!exists){
this.failedImages.push(imageLocation);
//console.log(this.failedImages);
}
// Notify the progress of the overall deferred. This is different
// than Resolving the deferred - you can call notify many times
// before the ultimate resolution (or rejection) of the deferred.
this.deferred.notify({
percent: Math.ceil( this.imageHandleCount / this.imageCount * 100 ),
imageLocation: imageLocation
});
//If image exists, push src into cleanedImages[],
if(exists){
this.cleanedImages.push(imageLocation);
console.log("success",imageLocation);
}else{
console.log("failed",imageLocation);
}
//if a full cycle on this.imageLocations has been achieved
if ( this.imageHandleCount === this.imageCount ) {
//console.log(this.cleanedImages);
if ( this.failedImages.length === this.imageHandleCount ) {
//console.log(this.cleanedImages);
this.state = this.states.REJECTED;
//return all the successful images
this.deferred.reject( imageLocation );
}else{
this.state = this.states.COMPLETED;
//return all the successful images
this.deferred.resolve( [this.cleanedImages, this.slideTitles] );
}
}
},
// I load the given image location and then wire the load / error
// events back into the preloader instance.
// --
// NOTE: The load/error events trigger a $digest.
loadImageLocation: function loadImageLocation( imageLocation ) {
var preloader = this;
// When it comes to creating the image object, it is critical that
// we bind the event handlers BEFORE we actually set the image
// source. Failure to do so will prevent the events from proper
// triggering in some browsers.
var image = $( new Image() )
.load(
function( event ) {
// Since the load event is asynchronous, we have to
// tell AngularJS that something changed.
$rootScope.$apply(
function() {
preloader.handleImage( event.target.src, true );
// Clean up object reference to help with the
// garbage collection in the closure.
preloader = image = event = null;
}
);
}
)
.error(
function( event ) {
// Since the load event is asynchronous, we have to
// tell AngularJS that something changed.
$rootScope.$apply(
function() {
preloader.handleImage( event.target.src, false );
// Clean up object reference to help with the
// garbage collection in the closure.
preloader = image = event = null;
}
);
}
)
.prop( "src", imageLocation );
}
};
// Return the factory instance.
return( Preloader );
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment