reptile7's JavaScript blog
Saturday, April 06, 2013
 
No Sulfur Suppression
Blog Entry #284

We continue today our discussion of Matt Gabbert's mouse fireworks script. We are currently working through the script's mouseDown( ) function: at this point we have
(1) clicked the mouse fireworks page and gotten the click's page coordinates,
(2) visibilized the © divs - absolutely positioned but not given left and top offsets, the divs are transiently rendered at a common normal-flow position, wherever that happens to be - and
(3) switched on the sparksAflyin flag, signifying that a fireworks burst is in progress.

The mouseDown( ) function concludes by iteratively calling the script's moveTo( ) function in order to send the © projectiles on their way.

for (i = 0; i <= 9; i++)
    eval('moveTo(' + i + ', 0, ' + mousex + ', ' + mousey + ')');


As in the script's SHOW( ) function, the above use of eval( ) is completely unnecessary:

for (i = 0; i <= 9; i++) moveTo(i, 0, mousex, mousey);

Moreover, the mousex and mousey values do not change during a fireworks burst and there would be no need to feed them to the moveTo( ) function had we declared mousex and mousey globally and not in the mouseDown( ) function, but let's keep going for the time being.

Projectile motion

Before the mouse fireworks page loads, the script's JavaScript defines twenty arrays - anim_0_x, anim_0_y, ... anim_9_x, anim_9_y - whose values are used by the moveTo( ) function to calculate and set left and top offsets for the © divs during a fireworks burst. Per their names, the arrays respectively adjust the x-axis and y-axis positions of the © divs, for example, the sDiv0 div's x-axis position is adjusted by the anim_0_x array's values and its y-axis position is adjusted by the anim_0_y array's values.

The mouseDown( ) function calls the moveTo( ) function ten times, once per © div. For each div the moveTo( ) function is recursively executed a variable number of (12-23) times depending on how many values are in the div's arrays, e.g., the anim_0_x and anim_0_y arrays each contain 12 values

anim_0_x = new Array(20, 20, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0);
anim_0_y = new Array(-20, -40, -60, -80, -60, -40, -20, 0, 20, 40, 60, 80);


and therefore moveTo( ) runs 12 times for the sDiv0 div. The number of array values is tracked by a j counter variable that is initialized to 0 and runs to the length of each array.

function moveTo(i, j, mousex, mousey) {
    if (j < eval('anim_' + i + '_x.length')) {
        ...
        // timeout: 50 = fireworks speed, larger number = slower speed
        window.setTimeout("moveTo(" + i + ", " + j + ", " + mousex + ", " + mousey + ");", 50); } ... }


Regarding the if condition, anim_#_x (unlike document.all and document.layers) is not a recognized collection expression and thus the preceding use of eval( ) might seem necessary; however, it occurred to me that formulating the div arrays as two-dimensional arrays would allow us to throw out the eval( ) operation even in this case*:

if (j < animx[i].length) {
...
animx[0] = [20, 20, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0];
animy[0] = [-20, -40, -60, -80, -60, -40, -20, 0, 20, 40, 60, 80];
...


*This insight was inspired by the two-dimensional array code appearing in HTML Goodies' "Build Your Own Image Viewer with Scrollbar" tutorial.

The eval( ) function crops up a few more times in the script; I will henceforth replace eval( ) operations with non-eval( ) equivalents.

After verifying that j is in the 0 to divArray.length-1 range, the moveTo( ) function calculates new tempx and tempy offsets for each © div by respectively adding an x/y pair of array values to the mousex/mousey click coordinates.

var tempx = animx[i][j] + mousex;
var tempy = animy[i][j] + mousey;


On the Netscape side, tempx and tempy are respectively assigned to the left and top properties of the © divs, which are layer objects by virtue of being absolutely positioned.

if (n) {
    document.layers["sDiv" + i].left = tempx;
    document.layers["sDiv" + i].top = tempy; }


On the IE side the offset-setting code is a bit more involved.
(1) If the tempx value will place the left edge of a © div inside a 30px-wide buffer abutting the right edge of the page, then tempx is decreased (pushed leftward) by 30.
(2) If the tempy value will place the top edge of a © div inside a 30px-high buffer abutting the bottom edge of the page, then tempy is decreased (pushed upward) by 30.
(3) Adjusted or not, tempx and tempy are then respectively assigned to the style.left and style.top properties of the © divs.

if (ie) {
    if (tempx + 30 > (document.body.offsetWidth + document.body.scrollLeft))
        tempx = document.body.offsetWidth + document.body.scrollLeft - 30;
    if (tempy + 30 > (document.body.offsetHeight + document.body.scrollTop))
        tempy = document.body.offsetHeight + document.body.scrollTop - 30;
    document.all("sDiv" + i).style.left = tempx + "px";
    document.all("sDiv" + i).style.top = tempy + "px"; }


When using IE 4.5 and IE 5.1.6, I find that document.body.offsetWidth and document.body.offsetHeight, which are meant to return the viewport dimensions in the above code,
(a) return the viewport dimensions if the document content area does not exceed the viewport, in which case there is no need to correct for user scrolling, and
(b) return the page dimensions if the document content area exceeds the viewport, in which case we do not want to correct for user scrolling.
Alternatively, document.body.clientWidth and document.body.clientHeight in quirks mode reliably return the viewport dimensions regardless of whether the document content area is larger or smaller than the viewport.

Although Netscape 4.x supports neither offsetWidth/offsetHeight nor scrollLeft/scrollTop nor document.body for that matter, the tempx and tempy offsets could have been similarly pre-adjusted in the corresponding Netscape code via:

if (tempx + 30 > window.innerWidth + window.pageXOffset)
    tempx = window.innerWidth + window.pageXOffset - 30;
if (tempy + 30 > window.innerHeight + window.pageYOffset)
    tempy = window.innerHeight + window.pageYOffset - 30;


You might not care about pre-adjusting tempx and tempy - after all, the user shouldn't expect to see a proper fireworks burst upon clicking next to the viewport's right or bottom edge - that said, the aforementioned clientWidth/clientHeight approach will keep the projectiles from moving off-viewport for the greatest number of users.

Subsequently the moveTo( ) function increments j (j++;) and then calls itself after a 50-millisecond delay (vide supra). In the following moveTo( ) run mousex/mousey are adjusted by the next x/y pair of array values, per the incremented value of j; in the moveTo( ) run after that we adjust mousex/mousey by the next x/y pair of array values; and so on. Suppose we click the page at (x,y) = (200,200). In its first moveTo( ) iteration the sDiv0 div is placed at (tempx,tempy) = (220,180), in its second moveTo( ) iteration the div moves to (tempx,tempy) = (220,160), in its third moveTo( ) iteration it moves to (tempx,tempy) = (210,140), etc.

Ending the show

When j maxes out at the divArray.length(s), an else clause (a) calls on the script's HIDE( ) function to hide each © div and (b) counts up the divs as they disappear via the totalSparks variable.

else {
    HIDE("sDiv" + i);
    totalSparks++; }
...
function HIDE(divName) {
    if (document.all)
        document.all(divName).style.visibility = "hidden";
    else if (document.layers)
        document.layers[divName].visibility = "hide"; }


The moveTo( ) function concludes by resetting sparksAflyin and totalSparks to 0 when totalSparks hits 10.

if (totalSparks == 10) {
    sparksAflyin = 0;
    totalSparks = 0; }


Demos

I don't know where Matt got the animx/animy array values - maybe he has a doctorate in pyrotechnics or something. The values move most of the © divs on roughly parabolic paths; to see the individual paths for the sDiv0, sDiv6, and sDiv8 divs, click the corresponding links in the div below.

Click here to see the sDiv0 div path. Click here to see the sDiv6 div path. Click here to see the sDiv8 div path.

Let me wrap up this post with one more demo. As cool as the previous post's ♥ demo was, it wasn't really very fireworks-y, was it? Click in the div below to see a display that more resembles an actual fireworks burst.

Baby, you're a firework...

This demo uses asterisks for its projectiles, gives the projectiles new colors that are suitable for a navy background, further increases the projectile size, and slows things down by doubling the setTimeout( ) delay to 100 milliseconds.
We may or may not be done with the Lissa Explains It All JavaScript subsector - we'll take stock of the situation in the following entry.

Comments:
Thank you so much. ♥

I loved this script back then, I'm so happy I can use it now!
 
Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)