How do image preloaders work?
Loading a single image
The browser will load images asynchronously...meaning when the browser is given the .src
of an image, it will start loading that image in the background, but will also continue processing the javascript code directly after .src
// create a new image object
var img=new Image();
// set the image object's image source
img.src='myImage.png';
// do some stuff while the image is loading in the background
alert('Your image has started loading, but is not yet complete');
The alert will display even before the image is fully loaded and ready to use.
So how do you know when the image is fully loaded and ready to use?
Answer: You can tell the browser to "call you back" when it finishes loading the image. You get "called back" by adding an "img.onload" function on the image object. Whenever the browser finishes loading the image, the browser will trigger the code in the "img.onload" function.
img.onload = theImageHasFinishedLoading;
function theImageHasFinishedLoad(){
alert('Your image has finished loading...you can use it now');
}
The complete image loading process will occur in this sequence:
// happens 1st
var img=new Image();
// happens 2nd: this callback is ASSIGNED, but NOT TRIGGERED yet
// until the image has fully loaded
img.onload = theImageHasFinishedLoading;
// happens 3rd
img.src='myImage.png';
// happens 4th
alert('Your image has started loading, but is not yet complete');
// happens 5th after the browser has fully loaded the image
// (The browser will call this function automatically because .onload was set)
function theImageHasFinishedLoad(){
alert('Your image has finished loading...you can use it now');
}
Pre-loading multiple images
To preload multiple images:
Create an array to hold all your image URLs and add your image URLs to this array.
// For example
var imageURLs=[];
imageURLs[0]='myImage.png';Create an array to hold all your image objects (==your actual images).
// For example
var imgs=[];Add
new Image
elements to the image object array (add onenew Image
for each URL in the URL array).//For example
imgs[0] = new Image();Set every image object's
.onload
callback to the same function.// For example
imgs[0].onload = theImageHasFinishedLoading;Use the image URL array to set the
.src
of each image to the individual url.// For example
imgs[0].src = imageURLs[0];In the
theImageHasFinishedLoading
callback, increment a counter every time a new image is successfully loaded.// For example
var counter=0;
function theImageHasFinishedLoading(){
counter++;
}
You will know that all images are fully loaded when the counter reaches the same length as your image URL array.
function theImageHasFinishedLoading(){
counter++;
if(counter>=imageURLs.length){
alert('All the images have successfully preloaded. Go use them now!');
}
}
Here's a full example code and a Demo:
window.onload=(function(){
// canvas related variables var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); var cw=canvas.width; var ch=canvas.height;
// put the paths to your images in imageURLs[] var imageURLs=[]; imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/face1.png"); imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/face2.png");
// the loaded images will be placed in imgs[] var imgs=[]; var imagesOK=0; startLoadingAllImages(imagesAreNowLoaded);
// Create a new Image() for each item in imageURLs[] // When all images are loaded, run the callback (==imagesAreNowLoaded) function startLoadingAllImages(callback){
// iterate through the imageURLs array and create new images for each for (var i=0; i<imageURLs.length; i++) { // create a new image an push it into the imgs[] array var img = new Image(); // Important! By pushing (saving) this img into imgs[], // we make sure the img variable is free to // take on the next value in the loop. imgs.push(img); // when this image loads, call this img.onload img.onload = function(){ // this img loaded, increment the image counter imagesOK++; // if we've loaded all images, call the callback if (imagesOK>=imageURLs.length ) { callback(); } }; // notify if there's an error img.onerror=function(){alert("image load failed");} // set img properties img.src = imageURLs[i]; } }
// All the images are now loaded // Do drawImage & fillText function imagesAreNowLoaded(){
// the imgs[] array now holds fully loaded images // the imgs[] are in the same order as imageURLs[]
ctx.font="30px sans-serif"; ctx.fillStyle="#333333";
// drawImage the first image (face1.png) from imgs[0] // and fillText its label below the image ctx.drawImage(imgs[0],0,10); ctx.fillText("face1.png", 0, 135);
// drawImage the first image (face2.png) from imgs[1] // and fillText its label below the image ctx.drawImage(imgs[1],200,10); ctx.fillText("face2.png", 210, 135);
}
}); // end window.onload
body{ background-color: ivory; }#canvas{border:1px solid red;}
<canvas id="canvas" width=400 height=200></canvas>
How does the javascript preloading work?
Your basic Javascript preloader does this:
var image = new Image();
image.src = '/path/to/the/image.jpg';
The way it works is simply by creating a new Image object and setting the src of it, the browser is going to go grab the image. We're not adding this particular image to the browser, but when the time comes to show the image in the page via whatever method we have setup, the browser will already have it in its cache and will not go fetch it again. I can't really tell you why whatever you have isn't working this way without looking at the code, though.
One interesting gotcha that is discussed in this question is what happens when you have an array of images and try preloading them all by using the same Image object:
var images = ['image1.jpg','image2.jpg'];
var image = new Image();
for(var x = 0; x < images.length; x++) {
image.src = images[x];
}
This will only preload the last image as the rest will not have time to preload before the loop comes around again to change the source of the object. View an example of this. You should be able to instantly see the second image once you click on the button, but the first one will have to load as it didn't get a chance to preload when you try to view it.
As such, the proper way to do many at once would be:
var images = ['image1.jpg','image2.jpg'];
for(var x = 0; x < images.length; x++) {
var image = new Image();
image.src = images[x];
}
Preloading images with JavaScript
Yes. This should work on all major browsers.
Image preloader javascript that supports events
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
});
Preloading CSS Images
I can confirm that my original code seems to work. I was casually sticking to an image with a wrong path.
Here's a test : http://paragraphe.org/slidetoggletest/test.html
<script>
var pic = new Image();
var pic2 = new Image();
var pic3 = new Image();
pic.src="images/inputs/input1.png";
pic2.src="images/inputs/input2.png";
pic3.src="images/inputs/input3.png";
</script>
Preloading then adding additional images after page load to an image gallery on button click
Generally you would create an image with JavaScript through either document.createElement('img')
or the Image()
constructor. Both result an in instance of an HTMLImageElement
.
With this, you'll have an image that is not connected to the DOM, so it's not visible to you or the user. You can use this element to load the image behind the scenes by setting the image' src
property.
Then by listening to the onload
event you can determine whenever the image has finished loading. From here you could continue your flow by adding the image to the DOM and, for example, fade it in with animation.
The example below shows this process in the form of a function that returns a Promise
. This promise will resolve whenever the load event has been triggered.
const preloadImage = src =>
new Promise(resolve => {
const image = new Image();
const onLoad = () => {
resolve(image);
};
image.addEventListener('load', onLoad, {once: true});
image.src = src;
});
Using it should be like this:
const src = 'http://example.com/my-image.jpg';
preloadImage(src).then(image => {
// Here the image has been loaded and is available to be appended, or otherwise.
document.body.append(image);
});
In your case you would loop over each image, call the function while passing the URL of the image, and append the image to the DOM when it's finished loading.
You can handle any animations, like fade-ins with CSS.
Real world implementation
So how should you implement this in your project? You'll need to start at the point where you create your images. Currently your images are created as strings. But strings are just strings, they aren't HTML elements, yet.
I'd recommend that you'll create a placeholder for each image. This placeholder could visually indicate that an image is loading and act as a wrapper for the image. Add this placeholder immediately to the pItems
element.
Then load the image for each Image
in your data.projects
array by calling the preloadImage
. Whenever the image is loaded, append it to the placeholder we've just created. You should now have the effect that first a placeholder is added and the images are starting to appear one by one.
The same logic should be applied for the load more loop.
...
}).then(function (data){
for (let i = 0; i < itemsStart; i++) {
// Create a <div class="placeholder"> which should act as a placeholder / wrapper.
const placeholder = document.createElement('div');
placeholder.classList.add('placeholder');
// Create the image based on the Image value.
// Whenever the image is loaded, add it to the placeholder.
const src = data.projects[i].Image;
preloadImage(src).then(image => {
placeholder.append(image);
});
// Immediately add the placeholder.
// This line doesn't wait for preloadImage to finish because preloadImage is asynchronous. Look into Promises if that is new to you.
pItems.append(placeholder);
}
...
});
From here you've got control over how the placeholder should look and any animations an image inside that placeholder should have.
Related Topics
Python Library for Rendering HTML and JavaScript
How to Call a Js Function Using Onclick Event
How to Window.Scrollto() with a Smooth Effect
Extract the Current Dom and Print It as a String, with Styles Intact
How to Prevent Pull-Down-To-Refresh of Mobile Chrome
Detect Double Tap on iPad or iPhone Screen Using JavaScript
Sorting HTML Table with JavaScript
Create an Array with Same Element Repeated Multiple Times
How to Stop JavaScript Foreach
Es6 Object Destructuring Default Parameters
Angular2 Router 2.0.0 Not Reloading Components When Same Url Loaded with Different Parameters
How to Resize HTML Canvas Element
Jquery Parallax Scrolling Effect - Multi Directional
Safari in iOS8 Is Scrolling Screen When Fixed Elements Get Focus
Wait for Background Images in CSS to Be Fully Loaded
How to Detect in iOS Webapp When Switching Back to Safari from Background
How to Get First and Last Day of the Current Week in JavaScript