PDA

View Full Version : Image Gallery Pre-Loading Issues



myyoungfamily
11-27-2007, 03:33 AM
Hey guys,
I'm working on a gallery script and the issue I'm having is with the pre-loader. Once an image has been loaded and the user has moved on to another and then decides to come back and click an image again, javascript tries to load the image again. It's only a split second blink of a load but it's annoying. Other scripts I've seen, like lightbox, seem to remember if an image has been previously loaded. What am I doing wrong?

Here's my javascript code:



//change the opacity for different browsers
function changeOpac(opacity, id) {
var object = document.getElementById(id).style;
object.opacity = (opacity / 100);
object.MozOpacity = (opacity / 100);
object.KhtmlOpacity = (opacity / 100);
object.filter = "alpha(opacity=" + opacity + ")";
}


function blendimage(divid, orgimgid, imageid, descid, description, imagefile) {

var speed = 80; //set the time to complete the fade
var ease = 75; //set a value to ease the fade ending

fullpath = 'images/full/';
orgpath = 'images/original/';
loadingid = 'loading';
loadingpic = '..images/loading.gif';

// --------------------------------------
var length = Math.round(speed / 10);
var timer = 0;

//start to preload the chosen image
newImage = fullpath+imagefile;
imgPreloader = new Image();
imgPreloader.src = newImage;

//set the current image as background
document.getElementById(divid).style.backgroundImage = "url(" + document.getElementById(imageid).src + ")";

//make image transparent
changeOpac(0, imageid);

//change the link to the original image
document.getElementById(orgimgid).href = orgpath+imagefile;

//change the description
document.getElementById(descid).childNodes[0].nodeValue = description;

//make the opacity on the loading div 50
changeOpac(50, loadingid);
//display the loading div
document.getElementById(loadingid).style.visibility = 'visible';


// once image is preloaded, resize image container
imgPreloader.onload = function(){

//hide the loading div
document.getElementById(loadingid).style.visibility = 'hidden';

//make new image
document.getElementById(imageid).src = newImage;

//fade in new image
for(i = 0; i <= 100; i++) {
var o = i+((speed/ease)*length);
setTimeout("changeOpac(" + o + ",'" + imageid + "')",(timer * length));
timer++;
}

}
}

jscheuer1
11-27-2007, 09:51 AM
Well, lightbox v 2.03 uses a slightly different order to things:


imgPreloader = new Image();

// once image is preloaded, resize image container
imgPreloader.onload=function(){
Element.setSrc('lightboxImage', imageArray[activeImage][0]);
myLightbox.resizeImageContainer(imgPreloader.width, imgPreloader.height);

imgPreloader.onload=function(){}; // clear onLoad, IE behaves irratically with animated gifs otherwise
}
imgPreloader.src = imageArray[activeImage][0];

But that may or may not have anything to do with what you are talking about. I'd be able to get a better idea and play with alternatives if I saw the whole script, or, better yet, had a link to the live page:

Please post a link to the page on your site that contains the problematic code so we can check it out.

myyoungfamily
11-27-2007, 02:05 PM
www.justrpics.com/youngfamily

John,
you'll recognize much of my code... :)

jscheuer1
11-27-2007, 04:45 PM
That's some pretty ugly code. It looks neat enough, but there are undeclared variables, and it doesn't even work in IE or Opera here after the first load of a given large image. What browsers have you been testing in?

The only way I can see to avoid the brief loading display (only happens in FF anyway, once the below mods are made) on already preloaded images would be to not preload them after they are already preloaded. To do that would require some kind of keeping track of which ones have been preloaded already and which have not. I would suggest an array. Also, it would require, skipping the 'loading' display for those images which have already been preloaded, and otherwise executing the rest of the 'onload' code directly after changing the image src of the larger image, without running the onload function for these already preloaded images.

Anyways, without fixing undeclared variables or much else, doing it like so will get it working in Opera and IE, while still working in FF:


function blendimage(divid, orgimgid, imageid, descid, description, imagefile) {

var speed = 80; //set the time to complete the fade
var ease = 75; //set a value to ease the fade ending

fullpath = 'images/full/';
orgpath = 'images/original/';
loadingid = 'loading';
loadingpic = '..images/loading.gif';

// --------------------------------------
var length = Math.round(speed / 10);
var timer = 0;

//set the current image as background
document.getElementById(divid).style.backgroundImage = "url(" + document.getElementById(imageid).src + ")";

//make image transparent
changeOpac(0, imageid);

//change the link to the original image
document.getElementById(orgimgid).href = orgpath+imagefile;

//change the description
document.getElementById(descid).childNodes[0].nodeValue = description;

//make the opacity on the loading div 50
changeOpac(50, loadingid);
//display the loading div
document.getElementById(loadingid).style.visibility = 'visible';

//start to preload the chosen image
newImage = fullpath+imagefile;
imgPreloader = new Image();

// once image is preloaded, resize image container
imgPreloader.onload = function(){

//hide the loading div
document.getElementById(loadingid).style.visibility = 'hidden';

//make new image
document.getElementById(imageid).src = imgPreloader.src;

//fade in new image
for(i = 0; i <= 100; i++) {
var o = i+((speed/ease)*length);
setTimeout("changeOpac(" + o + ",'" + imageid + "')",(timer * length));
timer++;
}
imgPreloader.onload = function(){return;};
}

imgPreloader.src = newImage;
}

myyoungfamily
11-27-2007, 05:17 PM
I'll try the enhancments...

Help me out, what are my undeclared variables?

jscheuer1
11-27-2007, 06:38 PM
An undeclared variable is any that is first defined within any given scope without the use (directly or indirectly) of the var keyword. So, in your blend function, we have at least these:


fullpath = 'images/full/';
orgpath = 'images/original/';
loadingid = 'loading';
loadingpic = '..images/loading.gif';


to use the var keyword directly in declaring those, would be:


var fullpath = 'images/full/';
var orgpath = 'images/original/';
var loadingid = 'loading';
var loadingpic = '..images/loading.gif';

to use it indirectly, it has to be use directly on at least one previous variable in a comma separated chain of variable declarations:


var fullpath = 'images/full/',
orgpath = 'images/original/',
loadingid = 'loading',
loadingpic = '..images/loading.gif';

or even:


var fullpath = 'images/full/', orgpath = 'images/original/', loadingid = 'loading', loadingpic = '..images/loading.gif';

When one doesn't do this one way or another, the variable is defined in the global scope, and may conflict with other items there, including other variables and elements with id's that match the variable name.

Even if the scope is intended to be global, it should be formally declared with var in the global scope, so as not to conflict with an element. Once declared globally, it can be defined later inside a function without the var keyword and will maintain its new value globally unless later defined again.

The scope of a variable is the level of recognition within a given function or set of nested functions that you need it to have.


var blah='Hi';
function doit(){
alert(blah)
}
doit();

will alert blah from the global scope, or Hi.


var blah='Hi';
function doit(){
var blah='Bye';
alert(blah)
}
doit();
alert(blah);

Now we get Bye as defined in the local scope of the doit() function, followed by a second alert of Hi, from the unaffected global scope.


var blah='Hi';
function doit(){
blah='Bye';
alert(blah)
}
doit();
alert(blah);

This time we get two Bye alerts, because doit()'s blah used the global scope.

jscheuer1
11-27-2007, 07:30 PM
See my previous post for a discussion about declaring variables. Here is what appears to be (in limited testing) a workable version of the function with an array to store already preloaded images, and code added to avoid showing the loading display for those so stored (I think I fixed all of the undeclared variables as well):


function blendimage(divid, orgimgid, imageid, descid, description, imagefile) {
if (!blendimage.loaded)
blendimage.loaded=[];

var speed = 80; //set the time to complete the fade
var ease = 75; //set a value to ease the fade ending

var fullpath = 'images/full/',
orgpath = 'images/original/',
loadingid = 'loading',
loadingpic = '..images/loading.gif',
l=false;



// --------------------------------------
var length = Math.round(speed / 10);
var timer = 0;

//set the current image as background
document.getElementById(divid).style.backgroundImage = "url(" + document.getElementById(imageid).src + ")";

//make image transparent
changeOpac(0, imageid);

//change the link to the original image
document.getElementById(orgimgid).href = orgpath+imagefile;

//change the description
document.getElementById(descid).childNodes[0].nodeValue = description;

//make the opacity on the loading div 50
changeOpac(50, loadingid);
//display the loading div

//start to preload the chosen image
for (var b=blendimage.loaded, i = b.length-1; i > -1; --i)
if(b[i]==imagefile)
l=true;
if(!l){
document.getElementById(loadingid).style.visibility = 'visible';
newImage = fullpath+imagefile;
imgPreloader = new Image();
blendimage.loaded[blendimage.loaded.length]=imagefile;
// once image is preloaded, resize image container
imgPreloader.onload = function(){

//hide the loading div
document.getElementById(loadingid).style.visibility = 'hidden';

//make new image
document.getElementById(imageid).src = imgPreloader.src;

//fade in new image
for(i = 0; i <= 100; i++) {
var o = i+((speed/ease)*length);
setTimeout("changeOpac(" + o + ",'" + imageid + "')",(timer * length));
timer++;
}
imgPreloader.onload = function(){return;};
}

imgPreloader.src = newImage;
}
else {
document.getElementById(imageid).src = fullpath+imagefile;

//fade in new image
for(i = 0; i <= 100; i++) {
var o = i+((speed/ease)*length);
setTimeout("changeOpac(" + o + ",'" + imageid + "')",(timer * length));
timer++;
}
}
}

myyoungfamily
11-27-2007, 08:47 PM
WOW, Thanks for the help John.

I pasted in you code and it works great but it doesn't seem to be remembering the pre-loading. Your code is live on my site www.justrpics.com/youngfamily if you want to see it.

jscheuer1
11-27-2007, 10:43 PM
Well, it seems to work on your site from here, did you try clearing your browser's cache? And, you never did tell me what browser you were using.

I do see a jumpiness still in FF, but no display of the loading image or of the word 'loading' once an image has been cached once. I'm looking at your wallpaper page only, for the time being.

myyoungfamily
11-27-2007, 11:13 PM
Ok, I think we're close. I've been viewing it in FF because that seemed to be where the problem was. Safari and Opera look good. I think the jumpiness has something to do with the image size. I'm gonna see if I can nail that down. Thanks again for the help with the pre-loader.

jscheuer1
11-28-2007, 05:56 AM
That jumpiness is almost certainly related to resizing and/or positioning the image. I tend to think positioning. I can't find where in the code that the image actually changes size or positioning. It would probably help me to figure it out if you could tell me where these things are being done.

myyoungfamily
11-28-2007, 03:44 PM
I'm 99% sure I've corrected any sizing issues. Yesterday I had css downsizing the larger image a little just to fit my page better. I've taken that out now and the images are displaying at their actual size. Everything looks good until you try to re-click on a pic in FF.

So, I've even taken out the pre-loader temporarily and FF is still jumpy. FF doesn't like something about:
1. Changing the current image to a background image
2. Changing the current image to 0% opacity
3. Changing the image source
4. Fading in the new image

I think it's #3. When I take out 3 & 4, it doesn't jump. It may just be a fault with this method? Can you think of why or another method to accomplish this?

jscheuer1
11-28-2007, 05:10 PM
The main problem with the jumpiness was changing the image to the background, and having:


.fullpic {
height: 330px;
background-color:#FFFFFF;
background-position:center;
text-align:center;
overflow:hidden;
}

If you were to use:


.fullpic {
height: 330px;
background-color:#FFFFFF;
background-position:center top;
text-align:center;
overflow:hidden;
}

instead, a major portion of the jumpiness will vanish, all of it really. There is still a problem in FF though of a momentary switch to the 100% opaque new image before fading in commences. I will have to investigate further what that might be from.

OK, the forum was down for a bit, so I was able to figure this last bit out while waiting for it to come back up. Where we had:


else {
document.getElementById(imageid).src = fullpath+imagefile;

//fade in new image
for(i = 0; i <= 100; i++) {
var o = i+((speed/ease)*length);
setTimeout("changeOpac(" + o + ",'" + imageid + "')",(timer * length));
timer++;
}
}

The image is getting loaded too quickly in FF. If we slow it down just a little:


else {
setTimeout(function(){document.getElementById(imageid).src = fullpath+imagefile;

//fade in new image
for(i = 0; i <= 100; i++) {
var o = i+((speed/ease)*length);
setTimeout("changeOpac(" + o + ",'" + imageid + "')",(timer * length));
timer++;
}},100);
}

That seems to do the trick. I made some other minor changes to the blendimage function, so here is my current version:


function blendimage(divid, orgimgid, imageid, descid, description, imagefile) {
if (!blendimage.loaded)
blendimage.loaded=[];

var speed = 80; //set the time to complete the fade
var ease = 75; //set a value to ease the fade ending

var fullpath = 'images/full/',
orgpath = 'images/original/',
loadingid = 'loading',
loadingpic = '..images/loading.gif',
l=false;

//make the opacity on the loading div 50
if(!blendimage.loadingidc)
blendimage.loadingidc=setTimeout(function(){changeOpac(50, loadingid);},10);

// --------------------------------------
var dur = Math.round(speed / 10);
var timer = 0;

//set the current image as background
document.getElementById(divid).style.backgroundImage = "url(" + document.getElementById(imageid).src + ")";

//make image transparent
changeOpac(0, imageid);

//change the link to the original image
document.getElementById(orgimgid).href = orgpath+imagefile;

//change the description
document.getElementById(descid).childNodes[0].nodeValue = description;


//start to preload the chosen image
for (var b=blendimage.loaded, i = b.length-1; i > -1; --i)
if(b[i]==imagefile)
l=true;
if(!l){
//display the loading div
document.getElementById(loadingid).style.visibility = 'visible';
newImage = fullpath+imagefile;
imgPreloader = new Image();
blendimage.loaded[blendimage.loaded.length]=imagefile;
// once image is preloaded, resize image container
imgPreloader.onload = function(){

//hide the loading div
document.getElementById(loadingid).style.visibility = 'hidden';

//make new image
document.getElementById(imageid).src = imgPreloader.src;

//fade in new image
for(i = 0; i <= 100; i++) {
var o = i+((speed/ease)*dur);
setTimeout("changeOpac(" + o + ",'" + imageid + "')",(timer * dur));
timer++;
}
imgPreloader.onload = function(){return;};
}

imgPreloader.src = newImage;
}
else {
setTimeout(function(){document.getElementById(imageid).src = fullpath+imagefile;

//fade in new image
for(i = 0; i <= 100; i++) {
var o = i+((speed/ease)*dur);
setTimeout("changeOpac(" + o + ",'" + imageid + "')",(timer * dur));
timer++;
}},100);
}
}

Notes: Your variable 'length' is a reserved word, I changed it to 'dur'. Making the loading division 50% opaque only needs to be done once. I made it a property of the blendimage function:


//make the opacity on the loading div 50
if(!blendimage.loadingidc)
blendimage.loadingidc=setTimeout(function(){changeOpac(50, loadingid);},10);


But even that could be removed, if its opacity were set in the style section.

Also, your style was kind of messed up here (corrected):


<link rel="stylesheet" type="text/css" href="http://www.justrpics.com/templates/includes/gallery.css" />
<!--
/*Credits: Dynamic Drive CSS Library */
/*URL: http://www.dynamicdrive.com/style/ */
-->
<!-- Do not edit IE conditional style below -->
<!--[if gte IE 5.5]>
<style type="text/css">
#motioncontainer {
width:expression(Math.min(this.offsetWidth, maxwidth)+'px');
}
</style>
<![endif]-->
<!-- End Conditional Style -->
<style ty . . .

myyoungfamily
11-28-2007, 05:44 PM
Hey, it's working great! John, you're da man! Thank's again....

P.S.
I'd love to see something like this end up in the DD gallery scripts

I've also started another thread "Javascript Fading with "Ease" like flash" that applies to this gallery, if you don't mind taking a look.

myyoungfamily
12-01-2007, 09:10 PM
John,
I've found another issue with the pre-loader...

Evidently some browsers save the images in the browser cache and some don't. So what's happening is, when a user returns to a page where images have been saved to cache, the javascript doesn't know that the images are cached and tries to reload every image. So, as soon as the javascript tries to load the pic the browser quickly responds and loads the cached image. Instead of getting a nice fade you see the loading screen for a split second. Is there a way to have javascript look through the cache?

jscheuer1
12-02-2007, 05:57 AM
I'm not sure what the latest code is you are working with, but from the latest I had (additions red):


if(!l){
//Test load status
var testload=new Image();
testload.src = fullpath+imagefile;
if(typeof testload.complete!='boolean' || !testload.complete)
//display the loading div
document.getElementById(loadingid).style.visibility = 'visible';
newImage = fullpath+ima . . .

myyoungfamily
12-03-2007, 05:51 AM
Well, here's my current code with you addition and it's still happening???


function blendimage(divid, orgimgid, imageid, descid, description, imagefile) {
if (!blendimage.loaded)
blendimage.loaded=[];

var fullpath = 'images/full/',
orgpath = 'images/original/',
loadingid = 'loading',
loadingpic = '..images/loading.gif',
l=false;

//make the opacity on the loading div 50
if(!blendimage.loadingidc)
blendimage.loadingidc=setTimeout(function(){changeOpac(50, loadingid);},10);

//set the current image as background
document.getElementById(divid).style.backgroundImage = "url(" + document.getElementById(imageid).src + ")";

//make image transparent
changeOpac(0, imageid);

//change the link to the original image
document.getElementById(orgimgid).href = orgpath+imagefile;

//change the description
document.getElementById(descid).childNodes[0].nodeValue = description;


//start to preload the chosen image
for (var b=blendimage.loaded, i = b.length-1; i > -1; --i)
if(b[i]==imagefile)
l=true;
if(!l){
//Test load status
var testload=new Image();
testload.src = fullpath+imagefile;
if(typeof testload.complete!='boolean' || !testload.complete){
//display the loading div
document.getElementById(loadingid).style.visibility = 'visible';
newImage = fullpath+imagefile;
imgPreloader = new Image();
blendimage.loaded[blendimage.loaded.length]=imagefile;
// once image is preloaded, resize image container
imgPreloader.onload = function(){

//hide the loading div
document.getElementById(loadingid).style.visibility = 'hidden';

//make new image
document.getElementById(imageid).src = imgPreloader.src;

fadeImage('in', 0, 100, 5, 3, 50, 0, 10, imageid);

imgPreloader.onload = function(){return;};
}
}
imgPreloader.src = newImage;
}else{
setTimeout(function(){
document.getElementById(imageid).src = fullpath+imagefile;
fadeImage('in', 0, 100, 5, 3, 50, 0, 10, imageid);
},100);
}
}

jscheuer1
12-03-2007, 07:07 AM
You copied my code wrong. I didn't indicate any bracketing like you have:


//Test load status
var testload=new Image();
testload.src = fullpath+imagefile;
if(typeof testload.complete!='boolean' || !testload.complete){
//display the loading div
document.getElementById(loadingid).style.visibility = 'visible';
newImage = fullpath+imagefile;
imgPreloader = new Image();
blendimage.loaded[blendimage.loaded.length]=imagefile;
// once image is preloaded, resize image container
imgPreloader.onload = function(){

//hide the loading div
document.getElementById(loadingid).style.visibility = 'hidden';

//make new image
document.getElementById(imageid).src = imgPreloader.src;

fadeImage('in', 0, 100, 5, 3, 50, 0, 10, imageid);

imgPreloader.onload = function(){return;};
}
}

You've cut out an entire section of the code. Get rid of the two red brackets.