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.
Related
I had written a callback function to capture the snapshot of running video using html5 video control and canvas.
I used a for loop to iterate and call the same callback function take the burst capture. If i add alert('') in the callback , the video in the background rerendering when alert message display, the burst snap shot works fine as taking diff photos(frames/images of the running video). But when I removed the alert('') , the video does not run in the background and the bursted images are the same instead of different.
The code
for (var i = 0; i < burstcount; i++) {
var wcam = Webcam;
wcam.burst_snap(function (dataurl, id) {
var arrayindex = passedName + "_" + id;
imgid = imgid + i;
alert(dataurl);
burstcapturedata[arrayindex] = dataurl;
}, i);
var j = 0;
while (j < 10000000000) {
j++;
}
}
DisplayBurstedImages();
}
Yes, actually. Alert holds next execution of code. If your code works with alert it means that you require delay.
Try setTimeout or put the code in the correct place where everything is getting loaded.
I guess it needs time for binding video. you can use setTimeout function for delay.
var delay =100;
setTimeout(function () {/*your function*/ }, delay);
delay = delay + 300;
Your code needs to look something like this:
var wcam = Webcam;
var cnt = 0;
wcam.burst_snap(function(dataurl, id) {
var arrayindex = passedName + "_" + id; //you do not have an array if this is the key
imgid = imgid + cnt; //no clue what this is for
cnt++;
burstcapturedata[arrayindex] = dataurl;
if (cnt===burstcount) {
DisplayBurstedImages();
}
}, 100); //no clue what that value is supposed to be
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"
)
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;
}
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.