reptile7's JavaScript blog
Friday, January 04, 2013
Vein of Snow Stars
Blog Entry #275

In today's post we'll take up the cross-browser "snowmaker" script offered by the How do I make snow fall on my Web site? subsection of Section 1 of the JavaScript subsector of Lissa Explains It All. The snowmaker script works for all browsers with document.getElementById( ) support; IE 4.x and Netscape 4.x are left behind, as they should be. The script uses asterisk characters instead of snow.gif images for its snowflakes - good enough, eh? A .zipped snow2instructions.txt file containing the script can be downloaded here. Lissa provides a snow2.html demo page for the script - this one will work for you, non-IE users.

The snowmaker script was written by Peter Gehrig in 2003. Peter used to maintain a personal Web site whose "Kostenlose DHTML und JavaScript Text Animationen" page served as a portal for his animation scripts; he deployed the snowmaker code in the portal's "Schneemelder" script. Peter also used to showcase his work at a site, which hosted and demonstrated the snowmaker script here. (Unlike the preceding resources, the site specified in the snow2instructions.txt script's //CREDITS: - view it here - had already gone belly up when this post was first written.)

Still more gatekeeperism

The snowmaker script uses feature detection and a bit of browser sniffing
(1) to condition the calling of the script's initsnow( ) function, which styles the script's snowflakes and strews them across the viewport, and
(2) to determine the viewport's dimensions at the beginning of the initsnow( ) function body.

var marginbottom, marginright;
var browserinfos = navigator.userAgent;
var ie5 = document.all && document.getElementById && !browserinfos.match(/Opera/);
var ns6 = document.getElementById && !document.all;
var opera = browserinfos.match(/Opera/);
var browserok = ie5 || ns6 || opera;

function initsnow( ) {
    if (ie5 || opera) {
        marginbottom = document.body.clientHeight;
        marginright = document.body.clientWidth; }
    else if (ns6) {
        marginbottom = window.innerHeight;
        marginright = window.innerWidth; } ... }

if (browserok) { window.onload = initsnow; }

An ie5 variable flags IE 5+; an ns6 variable flags Netscape 6+; an opera variable flags all versions of Opera. Let me first say that if you want to screen for Opera, there's no need to check the browser's navigator.userAgent return, let alone match( ) that return against an /Opera/ regular expression literal: a simple window.opera test will suffice*.
*JavaScript Kit now points out that window.opera will flag Opera (any version up until Opera 15).

But we actually don't need any of these flags. We can condition the initsnow( ) call with a document.getElementById gate and then measure the viewport with a

marginbottom = window.innerHeight ? window.innerHeight : document.body.clientHeight;
marginright = window.innerWidth ? window.innerWidth : document.body.clientWidth;

pair of statements.

Snowflake creation

The snowflakes are created by the following top-level code:

// Set the number of snowflakes (more than 30 - 40 not recommended)
var snowmax = 35;
// Set the letter that creates your snowflake (recommended:*)
var snowletter = "*";
// Set the maximal size of your snowflakes
var snowmaxsize = 22;

for (i = 0; i <= snowmax; i++) {
    document.write("<span id='s" + i + "' style='position:absolute;top:-" + snowmaxsize + ";'>" + snowletter + "</span>"); }

Asterisk (snowletter) snowflakes are wrapped in span elements so that we can apply CSS to them. The spans are
(a) respectively given ordinalized ids - s0, s1, s2, etc.;
(b) absolutely positioned; and
(c) initially pushed above the top edge of the viewport by a top:-22 declaration if we're running in quirks mode**.

**N.B. Peter does not include a px unit identifier for his CSS top, fontSize, and left assignments. The absence of a unit identifier for the left and top assignments in the script's movesnow( ) function stops the falling snow display if the browser is running in strict mode, i.e., if a valid document type declaration is placed at the top of the document. Although it is best if the script is run in quirks mode, I will nonetheless specify a px unit where relevant in the discussion that follows.

The position setting can be moved to a style sheet and the top setting is unnecessary:

span { position: absolute; }
for (i = 0; i <= snowmax; i++) document.write("<span id='s" + i + "'>" + snowletter + "</span>");

Two more points before moving on:
• The snowletter snowflake does not have to be a single character - see my demo below.
• As written above, the for loop creates 36 and not snowmax (35) snowflakes - nothing to panic over.

Typefacing, sizing, and coloring the snowflakes

The initsnow( ) function applies a random typeface, font size, and color to each snowflake before positioning it in the viewport area.

A typeface is set for each snowflake by the following code:

var snow = new Array( );
// Set the fonts for the snowflakes - add as many fonts as you like
var snowtype = new Array("Arial Black", "Arial Narrow", "Times", "Comic Sans MS");

function randommaker(range) {
    rand = Math.floor(range * Math.random( ));
    return rand; }

function initsnow( ) { ...
    for (i = 0; i <= snowmax; i++) { ...
        snow[i] = document.getElementById("s" + i);
        snow[i].style.fontFamily = snowtype[randommaker(snowtype.length)]; ... } ... }

A snowtype array of four typefaces is declared in the top-level part of the code. The initsnow( ) function feeds the snowtype.length to a randommaker( ) function that multiplies it by Math.random( ) and Math.floor( )s the result to give a rand random integer in the range 0 to 3, inclusive. The rand value is returned to the randommaker( ) call in order to serve as a snowtype array index for assigning a snowtype typeface to a snowflake's CSS fontFamily property. (Technically fontFamily is not a CSS property but an attribute of the Style DOM's CSS2Properties interface, but as a practical matter this is a distinction without a difference.)

A font size is set for each snowflake by the following code:

// Set the minimal size of your snowflakes
var snowminsize = 8;
var snowsizerange = snowmaxsize - snowminsize; // snowmaxsize, 22, is defined above
snow[i].size = randommaker(snowsizerange) + snowminsize;
snow[i].style.fontSize = snow[i].size + "px";

The snow[i].size range runs from 8 (inclusive) to 22 (exclusive).

In the HTML DOM, the span element is represented by the HTMLElement interface, which does not have a size attribute, but that doesn't mean we can't give our span objects a custom JavaScript size property, as we are doing here. BTW, if you're wondering why we don't directly assign randommaker(snowsizerange)+snowminsize to snow[i].style.fontSize, it's because the script has other uses for snow[i].size - vide infra.

A color is set for each snowflake by the following code:

// Set the colors for the snow - add as many colors as you like
var snowcolor = new Array("#aaaacc", "#ddddff", "#ccccdd");
snow[i].style.color = snowcolor[randommaker(snowcolor.length)];

The #aaaacc, #ddddff, and #ccccdd colors are all shades of lavender but do not have recognized color names.

Go look at the snow2.html demo page if you didn't do so earlier. Most users will not notice that the snowflakes have differing typefaces, at least not as long as the maximum snowflake font-size is 21px***; however, the differing snowflake font-sizes and colors do give a sense of depth to the display.

***If you beef up the font-size to 50px or so, you can definitely see the difference:
Arial Black: * Arial Narrow: * Times: * Comic Sans MS: *

Defining the snowingzone

Peter's script includes a snowingzone configuration variable whose value is used to demarcate the snowing field of action.
(1) If snowingzone is 1, then the snow will blanket the entire viewport area.
(2) If snowingzone is 2, then the snow will only cover the left half of the viewport area.
(3) If snowingzone is 3, then the snow will only cover the middle half**** of the viewport area (****as in the middle frame of a cols="25%,50%,25%" frameset).
(4) If snowingzone is 4, then the snow will only cover the right half of the viewport area.

// Set the snowing-zone
// Set 1 for all-over-snowing, set 2 for left-side-snowing
// Set 3 for center-snowing, set 4 for right-side-snowing
var snowingzone = 3;

The initsnow( ) function uses snowingzone and the marginright viewport width to set a random left offset for each snowflake - here is the relevant code for the 3 snowingzone that Lissa sets for the snow2.html demo page:

if (snowingzone == 3) { snow[i].posx = randommaker(marginright / 2 - snow[i].size) + marginright / 4; }
snow[i].style.left = snow[i].posx + "px";

The snowingzone width is obtained by dividing marginright by 2. The width is debited by snow[i].size and the resulting difference is passed to randommaker( ) to give a random integer in the range running from 0 to marginright/2-snow[i].size-1. The randommaker( ) return is 'shifted rightward' by marginright/4 to give the left offset, which is assigned first to a custom posx property and then to snow[i].style.left.

The other snowingzone posx values are determined similarly:

if (snowingzone == 1) { snow[i].posx = randommaker(marginright - snow[i].size); }
if (snowingzone == 2) { snow[i].posx = randommaker(marginright / 2 - snow[i].size); }
if (snowingzone == 4) { snow[i].posx = randommaker(marginright / 2 - snow[i].size) + marginright / 2; }

We'll have more to say about the -snow[i].size subtractions and their relation to the generation of a horizontal scrollbar later.

Vertical distribution

The initsnow( ) function gives each snowflake a random top offset via:

snow[i].posy = randommaker(2 * marginbottom - marginbottom - 2 * snow[i].size);
snow[i] = snow[i].posy + "px";

Given that 2x - x = x, the snow[i].posy definition should be written as:

snow[i].posy = randommaker(marginbottom - 2 * snow[i].size);

The snowflakes are kept out of a 2*snow[i].sizepx-thick buffer at the bottom of the viewport.

The initsnow( ) function concludes by calling a movesnow( ) function, which, as its name implies, gets the snowing action under way - I'll discuss movesnow( ), and also roll out that demo I promised you, in the following entry.

Comments: Post a Comment

<< Home

Powered by Blogger

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