So I have been recently developing a site, The problem is the backgrounds for each page are images, and as a result, on slower connections (which is the case of some of the target audience) the images load progressivly as they are downloaded, to resolve this I am trying to make a preloading page that does the following :
Loads the Images
Once the loading is done, redirects the user to the requested page
<script type="text/javascript">
<!--//--><![CDATA[//><!--
var images = new Array()
var count=0;
function preload() {
for (i = 0; i < preload.arguments.length; i++) {
images[i] = new Image()
images[i].src = preload.arguments[i]
}
if(count==4) {
window.location = "index.html";
}
}
preload(
"backgrounds/bg1.jpg",
"backgrounds/bg2.jpg",
"backgrounds/bg3.jpg",
"backgrounds/bg4.jpg"
)
//--><!]]>
The problem is it redirects directly (I assume that it just starts the download of the image then directly adds one to the counter variable, quickly reaching 4 and not giving the image the time to download.
Any ideas how I can either make it signal me when the images have finished downloading, or only execute the redirect after it has done downloading the images ?
You need to wait for the load event. It's quite simple:
function preload(images, timeout, cb) {
var cbFired = false,
remaining = images.length,
timer = null;
function imageDone() {
remaining--;
if(remaining === 0 && !cbFired) {
cbFired = true;
clearTimeout(timer);
cb();
}
}
function timerExpired() {
if(cbFired)
return;
cbFired = true;
cb();
}
for(var i = 0; i < images.length; i++) {
var img = new Image();
img.onload = imageDone;
img.onerror = imageDone;
img.src = images[i];
}
timer = setTimeout(timerExpired, timeout);
}
You need to check a few things so that users don't get stuck:
You need to wait for both load and error so that the page doesn't get stuck if an image fails to load.
You should set a maximum timeout.
Also, in your code, i was a global variable (no var declaration).
Here's how to use it:
var images = [ "backgrounds/bg1.jpg",
"backgrounds/bg2.jpg",
"backgrounds/bg3.jpg",
"backgrounds/bg4.jpg"];
preload(images, 10000 /* 10s */, function () {
window.location = 'next_page';
});
Modify your preloader so that it binds to the "onload" event of the Image object and when all callbacks are fired it redirects (untested sample code below):
var images = new Array()
var count = 0;
function preload() {
var numImages = preload.arguments.length
for (i = 0; i < numImages; i++) {
images[i] = new Image();
images[i].onload = doneLoading; // See function below.
images[i].src = preload.arguments[i];
}
function doneLoading() {
if (++count >= numImages) {
window.location = "index.html";
}
}
}
preload(
"backgrounds/bg1.jpg",
"backgrounds/bg2.jpg",
"backgrounds/bg3.jpg",
"backgrounds/bg4.jpg"
)
Related
I have a javascript which uses many images, and I want to load them all before the user starts clicking around. The images are a little bit big, so it takes a while. Here's my code:
startTest();
function preloadImages() {
console.log("preload images");
for(var i = 1; i <= 132; i++) {
var img = new Image();
images[i-1] = "images/"+i+".png";
img.src = images[i-1];
if(i == 132) {
img.load = doneLoading();
}
}
}
function doneLoading() {
console.log("done loading");
document.getElementById("laading").style.display = "none";
console.log("loading gone");
showReady();
}
function startTest() {
console.log("start test");
trialCount = 0;
document.getElementById("laading").style.display = "block";
preloadImages();
var hammertime = Hammer(document.body).on("touch", function(event) {
registerTouch(event);
});
startTestTime = +new Date();
console.log("end start test");
}
As you can see, startTest() is first called, which calls preload images. Here's the issue:
When I load the page in the browser, the javascript console indicates that "done loading" has been printed, however the spinny wheel on the tab in the browser shows that the webpage is still loading...
What's going on here? How can I figure out once all my images are done loading?
You need to check on each image load that the image is the last one to load. Check using a count variable (loaded).
function preloadImages() {
var loaded = 0;
for(var i = 0; i <= 132; i++) {
var img = new Image();
images.push("images/"+(i+1)+".png");
img.src = images[i];
img.onload = function(){
loaded++;
if(!(loaded < 132)){
// all images have loaded
doneLoading();
}
}
}
}
In order to do a pre-loading, you will have to keep checking the complete status for each image that is being downloaded. Since, you can't know beforehand how much time it will take for each image to download (depending on latency or size), you will have to implement a timer which will keep polling the complete status on each image.
Here is one possible algorithm:
var timer = null,
numImages = 132,
allImages = new Array;
function preLoadImages() {
document.getElementById('loadcaption').innerHTML = 'Loading images... ';
for (var i = 0; i < numImages; i++) {
var tmp = "images/" + i + ".png";
allImages[i] = new Image();
allImages[i].onLoad = imgOnLoad(tmp);
allImages[i].src = tmp;
}
timer = window.setInterval('checkLoadComplete()', 250);
}
function imgOnLoad(imgName) { window.status = 'Loading image... ' + imgName; }
function checkLoadComplete() {
for (var i = 0; i < allImages.length; i++) {
if (!allImages[i].complete) {
imgOnLoad(allImages[i].src);
document.getElementById('loadcaption').innerHTML = 'Loading images... ' + Math.round((i/allImages.length)*100,0) + ' %';
return;
}
}
window.clearInterval(timer);
document.getElementById('loadcaption').innerHTML = 'Completed.';
window.status = '';
}
Where loadcaption is a simple HTML element to show progress. (You can also use an animated gif for loading animation).
The idea is to keep checking the complete status on each image by calling checkLoadComplete by a timer. Once all images are (load) complete, the for loop exits and the timer is cleared.
You start the pre-loading process by calling preLoadImages(); function.
I have a set of images that are frames in an animation like: frame-1.jpg, frame-2.jpg and I have about 400 images.
What I want to do is preload these 400 images before the animation starts.
Usually when preloading images I use the following:
var images = [
'img/nav-one-selected.png',
'img/nav-two-selected.png',
'img/nav-three-selected.png',
'img/nav-four-selected.png'
];
$(images).each(function() {
var image = $('<img />').attr('src', this);
});
But in this instance, listing the images in the var isn't feasible, and I also wish to fire off the start animation once the images have all been loaded.
So far I have the following:
$spinner_currentFrame = 1;
$numFrames = 400;
function preloadImages() {
$($images).each(function() {
var image = $('<img />').attr('src', this);
});
startAnimation();
}
function startAnimation() {
$spinner_loadingAnim = setInterval(function () {
UpdateSpinner();
}, 140);
}
function UpdateSpinner() {
$spinner_currentFrame = $spinner_currentFrame + 1;
if($spinner_currentFrame > $numFrames) {
$spinner_currentFrame = 1;
}
console.log($spinner_currentFrame);
$('#spinner').css("background-image", "url(frame-" + $spinner_currentFrame + ".jpg)");
}
$(document).ready(function() {
preloadImages();
});
So the plan is that I preload images that are from 1 to 400 and then once that's completed then start the animation. How would I build the $images var though?
I've thought about something like:
$images = [];
$frame = 1;
$numFrames = 400;
$($frame).each(function() {
$frame = $frame + 1;
if($frame <= $numFrames) {
$images =+ 'frame-' + $frame + '.jpg';
}
});
But I'm not sure how a) efficient this is and b) how to do the callback once all images have loaded successfully.
You should use a standard javascript for loop instead of the jQuery's foreach. Foreach is wonderful for looping over an array or set of objects, but not in this case. Here is an example, please note that you have to bind the onload event handler before you set the Image object's src property.
UPDATE: added more functions to complete the entire example.
var loaded_images = 0;
var frames = 400;
var images = [];
function preloadImages() {
for (i=0; i < frames; i++) {
images[i] = new Image();
images[i].onload = function() {
loaded_images += 1;
checkLoadingFinished();
}
images[i].src = 'frame-' + i + '.jpg';
}
}
function checkLoadingFinished() {
if (loaded_images >= frames) {
startAnimation();
}
}
function startAnimation() {
var frameNumber = 0;
var timer = setInterval(function() {
$('#img-dom-element').attr('src', images[frameNumber]);
if (frameNumber > frames) {
frameNumber = 0;
else
frameNumber++;
}, (1000/30)); // (1000/30) = 30 frames per second
}
$(document).ready(function() {
preloadImages();
});
I dont know if it fits to your special case but I use
http://thinkpixellab.com/pxloader/
to preload images. You can add the paths and get one callback if all images are loaded. Afterwards you can start animation.
My application is a MVC, using MS SQL to get the image file names. I am using the following script to preload images; would like to know How I can display hourglass cursor while waiting to complete the preload.
function preload_images() {
var i, count = 0;
$('#selector').css('cursor', 'wait');
for (i = 1; i < sl; i++) {
images[i] = new Image();
images[i].onload = images[i].onerror = function () {
count++;
if (count == sl) {
$('#selector').css('cursor', 'default');
}
}
images[i].src = impath.toString() + im.toString() + i + ".jpg";
}
}
It works great first time, however, when I change the file name from a dropdownlist, the hourglass does not stop even all images are load. Thanks in advance.
From the code above you are loading sl-1 images, but the condition to stop the wait cursor is sl images(if (count == sl)), so change it to if (count == (sl-1))
Thanks to Musa for providing the initial solution, here is the way I got it to work, just in case someone is looking for it:
var images = [];
function preload_images() {
showLoadingImage();
$('#selector').css('cursor', 'wait');
for (i = tochar; i < sl; i++) {
images[i] = new Image();
var name = impath.toString() + imStart.toString() + i + ext;
images[i].src = 'ImageHandler.ashx?img=' + name + '&window=50&level=50 &slice=0';
if (i == sl - 1) {
images[i].onload = images[i].onerror = function () {
$('#selector').css('cursor', 'Default');
hideLoadingImage(this);
};
}
}
}
function showLoadingImage() {
$('#imagediv').append('<div id="imagediv1"><img src="../Content/images/ajax-loader2.gif" alt="Loading..." /></div>');
$('#imagediv1').show();
}
function hideLoadingImage(img) {
$('#imagediv1').hide();
img.onload = img.onerror = null;
}
What I'm trying to do is to change a background image 17 times, and then stop. When the user opens a new page, the same thing should happen, load 17 images and stop. The thing is, I don't know much about javascipt (I will learn I promise.) I found a script, it works, but I geuss I have to add a break. I tried but didn't succeed. Here's the code:
var imgArr = new Array(
// relative paths of images
'images/bgshow/1.jpg',
'images/bgshow/2.jpg',
'images/bgshow/3.jpg',
'images/bgshow/4.jpg',
'images/bgshow/5.jpg',
'images/bgshow/6.jpg',
'images/bgshow/7.jpg',
'images/bgshow/8.jpg',
'images/bgshow/9.jpg',
'images/bgshow/10.jpg',
'images/bgshow/11.jpg',
'images/bgshow/12.jpg',
'images/bgshow/13.jpg',
'images/bgshow/14.jpg',
'images/bgshow/15.jpg',
'images/bgshow/16.jpg',
'images/bgshow/17.jpg'
);
var preloadArr = new Array();
var i;
/* preload images */
for(i=0; i < imgArr.length; i++) {
preloadArr[i] = new Image();
preloadArr[i].src = imgArr[i];
}
var currImg = 1;
var intID = setInterval(changeImg, 150);
/* image rotator */
function changeImg() {
$('#page-wrap').animate({opacity: 0}, 0, function() {
$(this).css('background','url(' + preloadArr[currImg++%preloadArr.length].src +') top center no-repeat');
}).animate({opacity: 1}, 0);
}
Would appreciate your help a lot!
That script will keep changing images every 150ms, due to the setInterval(changeImg, 150) call. Thus, to stop it, you need to clear the interval when you are done changing all your images. This can be done at the end of your changeImg function, add the following;
function changeImg() {
// animation code...
if (currImg == preloadArr.length) {
clearInterval(intID);
}
}
if (currImg == preloadArr.length) run not clearInterval(intID); always currImg == 0;
In fact; currImg++ add Before or "if (currImg++ == preloadArr.length)"
function changeImg() {
// animation code...
if (currImg++ == preloadArr.length) {
clearInterval(intID);
}
}
I have this banner rotator which is working fine except for one problem...
This rotator here first goes through the function and when it reaches the "setTimeout" statement it triggers the "cycle" function again and again.
You guys probably know that in Firefox there is a status-bar in the bottom-left corner, which says "loading" or "waiting for domain.com..." etc etc.
The problem is this; When you enter the website in Firefox, the status bar says "Loaded" and dissappears. Then after 8 seconds (the setTimeout delay) the status bar shows again and displays something like "Getting data from domain.com...". And it doesn't go away.
This message is most likely caused by the setTimeout code, which triggers the function cycle over and over again.
Is there any way of "solving" this?
This probably happens in other browsers also, but so far I have only tested it in FF.
function ban_rot(){
//First preload images
// counter
var i = 0;
// create object
imageObj = new Image();
// set image list
images = new Array();
images[0] = "../Graphics/adv/1.gif"
images[1] = "../Graphics/adv/2.jpg"
// start preloading
for (i = 0; i <= images.length; i++) {
imageObj.src = images[i];
}
///////////////////////
var links = new Array("http://www.link1.com", "http://www.link2.se");
var alts = new Array("alt1", "alt2");
var titles = new Array("title1", "title2");
var counter = 0;
var banner_div = document.getElementById("ban_rot");
cycle();
function cycle() {
if (counter == links.length) {
counter = 0;
}
else if (counter < links.length) {
banner_div.innerHTML = '<img src=\"' + images[counter] + '\" border=\"1px\" style=\"border-color:#000;\" alt=\"' + alts[counter] + '\" title=\"' + titles[counter] + '\">';
//increase counter
counter++;
}
setTimeout(cycle, 8000);
} //end cycle function
} //end ban_rot function
The browser is showing a loading indicator because you're making it load a new image.