Forum Moderators: open
BTW, much thanks to Bernard Marx for helping me to understand how better to do the setInterval loop [webmasterworld.com] and for the Thing reference. :)
Here's the one that makes things larger.
function makeBig(image_handle, max)
{
var the_image;
Thing1 = setInterval(smoothGrow, 100);
function smoothGrow()
{
t = document.images[image_handle];
t.height = t.height + 8;
t.width = t.width + 8;
if (t.height >= max) {
clearInterval(Thing1);
t.height = max;
t.width = max;
}
}
}
The second function (makeSmall) is identical except for "+ 8" becomes "- 8" and the test for clearInterval is a <= test. They work as expected when the functions are called from the image itself, like so:
<a href="#" onClick="alert('Thing 1!');" onMouseOver="makeBig('1','300');" onMouseOut="makeSmall('1','96');">
<img src="cthings1.jpg" width="96" height="96" /></a>
However, when I add more images and/or rapidly move the mouse from one image to another, I start to get very, very jerky animation and the images get "stuck" at different portions of their growth or shrinking. Even with a single image, the onMouseOver and onMouseOut can both get triggered resulting in the image growing, shrinking, then growing again and sticking on the large version.
Is there a handler other than onMouseOut that I could use? I expect my final number of images would be about 20 which need this functionality.
I eventually want the ability to do something like this, where one image can partially grow it's neighbors:
onMouseOver="makeBig('1','300');makeBig('2','150');" But right now that is so screwed up I'm not sure how to make it work.
We need to keep certain state variables separate for each object being animated. This is acheived in part by the closure created by the inner function. It's very nearly as if we are creating 'instances' of execution of makeBig. Each time it is called, the variables within are still 'alive', even after the function returns. The inner function 'lives' inside it.
This means:
1. You might as well set t = document.images[..] at the top of the outer function. The variable, the_image, would have done nicely ;)
2. Need local vars, otherwise they are changed on every call, leading to non-independant behaviour (to coin a phrase). This includes the reference to the returned value of setInterval. This could be a local variable, but we might as well keep hold of it. Here, I attach it to the image itself.
This is a version of growBig. Really, it needs a check to see if it's already running, but....
<html><head><title>? </title>
<style>
img{cursor: pointer; cursor: hand;position: absolute; top: 100px;}
#thing0{left: 50px;}
#thing1{left:350px;}</style>
<script>
function makeBig(id, max)
{
var image = document.getElementById(id); ;
image.timer = setInterval(smoothGrow, 100);
function smoothGrow()
{
image.height += 8;
image.width += 8;
if (image.height >= max)
{
clearInterval(image.timer);
image.height = image.width = max;
}
}
}
</script></head>
<body><img onclick="makeBig('thing0',200)"
id="thing0" src="anImage0.gif"
width = "20" height = "20" border="0" /><img onclick="makeBig('thing1',250)"
id="thing1" src="anImage1.gif"
width = "20" height = "20" border="0" /></body>
</html>
Really, it needs a check to see if it's already running, but....
I'd at one point played around with setting a CSS
class="false"flag while the makeBig was working to keep the makeSmall from attempting. The default was
class="true". (I saw that in another thread here at WW and it looked pretty darn handy.)
The makeSmall used something like this wrapped around it's entire guts:
function makeSmall(...){
...setInterval stuff...
function smoothShrink() {
t = document.images[image_handle];
if (t.className) {
...all of resizing happens here...
}
}
Is that the sort of thing you are talking about? I was having problems that I attributed to this method, so I had abandoned it.
Mmm yeah, but there's no need for that (I'm thinking). Setting a class attribute has a couple of drawbacks:
1. Interferes with the use of className for styling.
2. Must cause a little cpu flutter while the browser works out whether or not to apply any style rules (this is guesswork).
Myself, I'd just go for a javascript property (of one's own free choice).
element.flag = true; This is what I've done in the functions below. But, if the functions are combined into (or are) one, then a local variable (outer function) could be used instead. This would probably be more efficient, especially in IE, which confuses element JS properties & element attributes, treating them the same.
My version ignores further calls to each function if its timer is active. But if, for eg, the element is growing, and shrink is called then the grow-timer is stopped, and the element can shrink back down. For this the flag needs to be not just true or false, but should indicate which direction is currently active, or hold a false value.
Test this in Mozilla first.
-------------- html ------------------------------
<html><head><title>? </title>
<style>
a{cursor: pointer; cursor: hand; font-family: verdana;}
#d0,#d1{position: absolute; top: 100px;}
#d0{left: 50px;}
#d1{left:350px;}
</style>
<script src="timers.js"></script>
</head>
<body><div id="d0">
<a onclick="makeBig ('thing0',200)">bigger</a>
<a onclick="makeSmall('thing0',20)">smaller</a>
<br>
<img id="thing0" src="../_gen/gallery/girl01.gif" width="20" height="20" border="0" />
</div><div id="d1">
<a onclick="makeBig ('thing1',250)">bigger</a>
<a onclick="makeSmall('thing1',50)">smaller</a>
<br>
<img id="thing1" src="../_gen/gallery/girl02.gif" width="50" height="50" border="0" />
</div>
</body>
</html>-------------- timers.js ------------------------------
!change these [b][red]¦¦[/red][/b] to unbroken pipes before running
// In both fns,
// 1st arg can be id string
// or an object reference.
function makeBig(ref, max)
{
var image = document.getElementById(ref)¦¦ref ;
if(image.action == 'grow') return;
clearInterval(image.timer);
image.timer = setInterval(repeat, 100);
image.action = 'grow';
function repeat()
{
image.height += 8;
image.width += 8;
if (image.height >= max)
{
clearInterval(image.timer);
image.timer = image.action = null ;
image.height = image.width = max;
}
}
}function makeSmall(ref, min)
{
var image = document.getElementById(ref)¦¦ref ;
if(image.action == 'shrink') return;
clearInterval(image.timer);
image.timer = setInterval(repeat, 100);
image.action = 'shrink';
function repeat()
{
image.height -= 8;
image.width -= 8;
if (image.height <= min)
{
clearInterval(image.timer);
image.timer = image.action = null;
image.height = image.width = min;
}
}
}//-------------------------------------------------------------
The functions are so similar that they could be merged into one with the addition of an extra argument, and some messing about inside (which would make things messy, so I didn't).
BUT..
This is fine with Mozilla. I have also used the same approach in (kind of) Flash type animations without bother. Yet, when I test old my old Win 98 with IE5, as soon as any interactivity is thrown in, such as here, IE will handle a few clicks, then give me a fatal error message and shut down.
My platform may be unstable to an extent, but this precise behaviour is only seen when I use this combination of fast-as-poss looping, closures and interactivity. It may be fine elsewhere too, but it does make me suspicious, and wonder whether it's good for general purpose use. After all, someone else may have as dodgy an IE5 as I do.
Maybe it's best to stick to the 'other' form of timeout usage - which is unfortunately considerably more complicated when it comes to functionality like this.