reptile7's JavaScript blog
Saturday, April 12, 2014
 
A Visit to the Visible Region, Part 2
Blog Entry #316

We continue today our deconstruction of the Java Goodies "Color Gradient Text" script. Having called the gradient( ) function and successfully gotten through a browser-checking gate, we next convert the thecolors string data into a structure that facilitates the creation of the script's gradient effect. We begin this process by feeding the thecolors string to a ColorList( ) constructor function. The ColorList( ) function constructs an Object object that is given a colors identifier.

var colors = new ColorList(thecolors);

We will use the first gradient( ) call's ff0000 ffffff 0000ff thecolors string in the discussion that follows.

The ColorList( ) function starts innocently enough by giving thecolors a new hexcodes identifier and declaring i and c variables.

function ColorList(hexcodes) { var i = 0; var c = 0;

Subsequently ColorList( ) defines a codes property as a three-element array, one element for each hexcodes color.

this.codes = new Array(Math.round(hexcodes.length / 7));

The ff0000 ffffff 0000ff string's length is 20, whose division by 7 gives 2.857142, which is Math.round( )ed up to 3.

Next we have a while loop that iterates over the hexcodes colors*

while (i < hexcodes.length) { ... }

and populates the codes boxes with child Object objects carrying the hexcodes data. (*Although the i < hexcodes.length condition seems to indicate that the loop iterates over the hexcodes characters, the loop actually leapfrogs from color to color via an i += 7; statement.) The while loop body comprises an if...else statement whose if clause checks if the i-to-i+6 hexcodes substring( ) - that would be ff0000 for the loop's first iteration - begins with a hexadecimal number.

if (isNaN(parseInt(hexcodes.substring(i, i + 6), 16))) ++i;

Go here for the current parseInt( ) page in the Mozilla JavaScript Reference; note that the parseInt( ) radix parameter is set to 16 (Mozilla exhorts authors to always set the radix value, even for a decimal operand). The parseInt( ) operation would return NaN ("Not-A-Number") - and therefore the isNaN( ) operation/if condition would return true - if we had prefaced ff0000 with a hash (#) mark, in which case the ++i; incrementation would push us to the starting f of ff0000 for the next iteration. (The if condition would also return true for a Hello World! hexcodes string, but let's assume that such strings are not on the menu, shall we?)

Given the hexcodes we're working with, however, the accompanying else clause is operative.

else { this.codes[c] = new ColorCode(hexcodes.substring(i, i + 6)); i += 7; ++c; }

The else clause sends ff0000 to a separate ColorCode( ) construction function.

function ColorCode(hexcode) { if (hexcode.length == 7) { this.r = parseInt(hexcode.substring(1, 3), 16); this.g = parseInt(hexcode.substring(3, 5), 16); this.b = parseInt(hexcode.substring(5, 7), 16); } else if (hexcode.length == 6) { this.r = parseInt(hexcode.substring(0, 2), 16); this.g = parseInt(hexcode.substring(2, 4), 16); this.b = parseInt(hexcode.substring(4, 6), 16); } else { this.r = this.g = this.b = 0; window.alert("Error: ColorCode constructor failed"); } if (isNaN(this.r) || isNaN(this.g) || isNaN(this.b)) window.alert("Error: ColorCode constructor failed"); }

The else if (hexcode.length == 6) clause is operative. The substring( ) calls grab the RR (ff), GG (00), and BB (00) parts of ff0000. The parseInt( ) calls respectively convert the ff/00/00 substring( )s to 255, 0, and 0**, which are respectively assigned to new r, g, and b properties of the ColorCode( ) object. (**A successful parseInt( ) operation returns a decimal integer regardless of its radix setting.)

The if (hexcode.length == 7) clause would not be operative if the hexcodes colors were prefaced with #s because
(a) the if clause in the ColorList( ) while loop would effectively subtract those #s and, more fundamentally,
(b) we don't pass more than six characters at a time to ColorCode( ) anyway.

It is left to the reader to verify that:
• The else clause that concludes the if...else if...else cascade would fire if the hexcodes string did not comprise 7n+6 characters (n=0,1,2,...), for example, f00 fff 00f would trigger it.
• The final if clause would fire if the (index-wise) second or fourth hexcode character were not a hexadecimal digit, e.g., ffg000 would trigger it.

Suffice it to say that if we check our work and make sure that the hexcodes string conforms to the original hexcode0 hexcode1 hexcode2 ... format - that's not too much to ask, is it? - then the else if (hexcode.length == 6) clause is the only one we need and the rest of it can be thrown out.

Returning to the else clause in the ColorList( ) while loop, the new ColorCode( ) object is assigned to colors.codes[0]. The loop's first iteration concludes by incrementing i to 7 and c to 1.

The loop's second iteration sends ffffff to the ColorCode( ) function and thereby produces a colors.codes[1] object with r, g, and b properties all set to 255. The loop's third iteration sends 0000ff to the ColorCode( ) function and produces a colors.codes[2] object whose r and g values are 0 and whose b value is 255. At the end of the third iteration i is pushed to 21; the loop stops when the i < hexcodes.length condition returns false at the beginning of (what would be) the fourth iteration.

For its part, the c index maxes out at 3; c's final value is assigned to a new len property at the end of the ColorList( ) function. (Unlike the Array object, the Object object does not have a length property.)

...while loop...
this.len = c; } // That's it for ColorList( )


In sum, the colors data structure is:

colors.codes[0].r = 255;
colors.codes[0].g = 0;
colors.codes[0].b = 0;
colors.codes[1].r = 255;
colors.codes[1].g = 255;
colors.codes[1].b = 255;
colors.codes[2].r = 0;
colors.codes[2].g = 0;
colors.codes[2].b = 255;
colors.len = 3;


Now, if all of the above seems like overkill to you, well, you would be right about that. The ColorList( )/ColorCode( ) functionality can be reproduced with:

var colors = thecolors.split(" "); var codes = new Array(colors.length); for (i = 0; i < colors.length; i++) { codes[i] = new Object( ); codes[i].r = parseInt(colors[i].substring(0, 2), 16); codes[i].g = parseInt(colors[i].substring(2, 4), 16); codes[i].b = parseInt(colors[i].substring(4, 6), 16); }

The thecolors string can be split into its constituent colors via the split( ) method of the String object. The codes array of Object objects can stand independently: there is no need to yoke it as a property to the colors object.

We will apply the r/g/b data to the thetext string in the following entry.

Comments: Post a Comment

<< Home

Powered by Blogger

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