reptile7's JavaScript blog
Monday, September 29, 2014
Planning a Color Trip
Blog Entry #336

We return now to our discussion of the Text Fader 1.3 script and its various functions. Today's post will be largely devoted to the fadeText( ) function, which carries out a set of foundational calculations in support of the script's fading operations. Its name notwithstanding, the fadeText( ) function doesn't actually fade the movie strings - the changeColor( ) function does that.

fadeText( ) parameterization

In the previous post we set the script functions in motion by calling the layout( ) function and then moved the fadeMe01 div to the center of the viewport via the getMid( ) and moveLayer( ) functions. In the layout( ) function the moveLayer("fadeMe01", midY - 50, midX - 200); call is followed by four calls to the fadeText( ) function, which are spread out timewise by window.setTimeout( ) commands.

fadeText("fadeMe01", "<span class='content'><b><a href=''>Teg WoRKz</a></b> Interactive</span><div class='small'>proudly presents</div>", "000000", "CCCCCC", 10, 2);

window.setTimeout("fadeText('fadeMe01', '<span class=\"content\"><b>Teg WoRKz</b> Interactive</span><div class=\"small\">proudly presents</div>', 'CCCCCC', '000000', 10, 4);", 8000);

window.setTimeout("fadeText('fadeMe01', '<div class=\"small\">A</div><span class=\"content\"><b>Teg WoRKz</b> Production</span>', '000000', 'CCCCFF', 10, 2);", 12000);

window.setTimeout("fadeText('fadeMe01', '<div class=\"small\">A</div><span class=\"content\"><b><a href=\"\">Teg WoRKz</a></b> Production</span>', 'CCCCFF', '000000', 10, 4);", 20000);

N.B. The quote formatting of the delayed fadeText( ) calls can be simplified by using the func, delay setTimeout( ) syntax, e.g.:

window.setTimeout(function ( ) { fadeText("fadeMe01", "<span class='content'><b>Teg WoRKz</b> Interactive</span><div class='small'>proudly presents</div>", "CCCCCC", "000000", 10, 4); }, 8000);

Each fadeText( ) call passes six arguments to the fadeText( ) function.

function fadeText(obj, str, rgb1, rgb2, speed, step) { ... }

(0) arguments[0] is the id of the div whose text will be faded, and is given an obj identifier.
(1) arguments[1] is a text+HTML string that will be loaded into the obj div, and is given an str identifier.
(2) arguments[2] is the str text's starting color in the form of a #-less six-digit RRGGBB value, and is given an rgb1 identifier.
(3) arguments[3] is the str text's ending color in the form of a #-less six-digit RRGGBB value, and is given an rgb2 identifier.
(4) arguments[4] will serve as the setTimeout( ) delay for a recursive changeColor( ) call, and is given a speed identifier.
(5) arguments[5] controls the gradualness of the rgb1rgb2 transition, and is given a step identifier.

Having gone through this, I should note that the fadeText( ) function does not in fact act on the obj, str, and speed arguments but merely passes them on to the changeColor( ) function.

More CSS

Each of the above str strings contains a span with a class='content' identifier and a div with a class='small' identifier; these elements are styled with:

.content { font: 16pt Verdana, Arial, Helvetica; width: 400px; text-align: center; }
.small { font: 6pt Verdana, Arial, Helvetica; width: 400px; text-align: center; }

The width and text-align properties ordinarily do not apply to span elements - we can make them apply if we give the spans an inline-block display although there's no burning reason to do so - the .content rule's width:400px; declaration can be thrown out and its text-align:center; declaration can be applied to the fadeMe01 div.

#fadeMe01 { position: absolute; text-align: center; width: 410px; }
.content { font: 16pt Verdana, Arial, Helvetica; }

• Link underlines are subtracted via an a { text-decoration:none; } rule.
• Oddly, b elements are styled with a b { font-weight:bold; } rule. Are there any situations in which <b></b> and font-weight:bold; give different renderings? Maybe, I don't know - I suppose there's no harm in keeping the rule but I'd get rid of it.

Slice and decimalize

The fadeText( ) body initially declares an r1/g1/b1/r2/g2/b2 set of variables.

var r1, g1, b1, r2, g2, b2;

Next, fadeText( ) splits the rgb1 and rgb2 colors into their RR, GG, and BB components, which are decimalized via an external hexToDec( ) function; the decimal R/G/B values are assigned to the r1-b2 variables.

for (cnt = 1; cnt <= 2; cnt++) { eval("r" + cnt + " = hexToDec(rgb" + cnt + ".slice(0, 2));"); eval("g" + cnt + " = hexToDec(rgb" + cnt + ".slice(2, 4));"); eval("b" + cnt + " = hexToDec(rgb" + cnt + ".slice(4, 6));"); } ... function hexToDec(hex) { var value = 0; while (true) { if (convert[value] == hex) break; value++; } return value; }

We'll address the form of this code in a moment, but first, here's what's going on in more detail:

(1) As far as I am aware we've never worked with the slice( ) method of the String object before. In the present case slice( ) behaves just like substring( ), e.g., "Hello World".slice(0, 2); extracts/returns the He section of Hello World, i.e., the section running from the 0th character up to but not including the 2nd character indexwise. Each rgb+cnt.slice( ) operation gives an RR|GG|BB value that is passed to the hexToDec( ) function and given a hex identifier.

(2) The hexToDec( ) function runs through the convert database in order (convert[0], convert[1], ...) via a while loop until it finds a convert[value] value that equals hex, at which point a break statement terminates the loop and value is returned to the fadeText( ) function, which assigns the hexToDec( ) return to r|g|b+cnt.

The for loop with its cnt counter/index and the eval( ) function allow us to 'ordinalize' the code and thereby make it more compact than it otherwise would be. However, we really should be getting rid of the eval( ) calls. Is it that much more effort to write out six lines of code for the r1-b2 assignments? No, it isn't.

r1 = hexToDec(rgb1.slice(0, 2));
g1 = hexToDec(rgb1.slice(2, 4));
b1 = hexToDec(rgb1.slice(4, 6));
r2 = hexToDec(rgb2.slice(0, 2));
g2 = hexToDec(rgb2.slice(2, 4));
b2 = hexToDec(rgb2.slice(4, 6));

Moreover, the hexToDec( ) function is redundant in that the rgb1/rgb2 RR/GG/BB values can be decimalized via the parseInt( ) function (h/t commoner and the Java Goodies Color Gradient Text script).

r1 = parseInt(rgb1.slice(0, 2), 16);
g1 = parseInt(rgb1.slice(2, 4), 16);
b1 = parseInt(rgb1.slice(4, 6), 16);
r2 = parseInt(rgb2.slice(0, 2), 16);
g2 = parseInt(rgb2.slice(2, 4), 16);
b2 = parseInt(rgb2.slice(4, 6), 16);

Pre-fade increments

The Text Fader 1.3 script is designed to change the R/G/B components of the rgb1 color to their rgb2 counterparts
(a) in a proportional manner and
(b) as gradually as is practically possible.
Toward this end, the script looks at the r1r2, g1g2, and b1b2 'color distances' and then bases the changeColor( ) fading process on the largest of those distances.

When the for loop has finished executing, control passes to a with block whose first three statements copy the r1, g1, and b1 values to s2r, s2g, and s2b variables; in the changeColor( ) function s2r/s2g/s2b will hold the RR/GG/BB color values of the str string as it fades.

/* These variables are declared globally just before the hexToDec( ) function. */
var s1, s2, s3, s2r, s2g, s2b, smallest;

with (Math) { s2r = r1; s2g = g1; s2b = b1; ... }

As you may know, Mozilla discommends the use of the with statement. Losing the with code will require us to laboriously prepend M, a, t, h, . to some up-and-coming abs( ) commands, but if you've come this far in the discussion you should be up to the task of doing that. ;-)

Moving on, the r1/g1/b1r2/g2/b2 color distances are calculated by:

s1 = Math.abs(r1 - r2);
s2 = Math.abs(g1 - g2);
s3 = Math.abs(b1 - b2);

The s1/s2/s3 values compose a ratio in proportion to which we will increment the rgb1 R/G/B components. At this point the s1-s3 numbers are not so useful. Let's change that, shall we?

An if...else if...else construct determines whether the s1, s2, or s3 distance is largest and assigns that distance to a smallest variable.

if (s1 > s2 && s1 > s3) { smallest = s1; }
else if (s2 > s1 && s2 > s3) { smallest = s2; }
else { smallest = s3; }

This code breaks down if two of the distances are equal and larger than the third distance, e.g., for a "0000FF""FFFFFF" (blue to white) transition, in which case the smaller distance is assigned to smallest: changing the > operators to >= solves this problem.

Division of s1/s2/s3 by smallest converts them to a minimized while normalized-to-1 form.

s1 = (s1 == 0) ? 0 : s1 / smallest;
s2 = (s2 == 0) ? 0 : s2 / smallest;
s3 = (s3 == 0) ? 0 : s3 / smallest;

Testing if s1, s2, or s3 is 0 becomes necessary only if smallest itself is 0, which would happen if rgb1 and rgb2 were equal, which should not be the case.

The resulting s1/s2/s3 values are multiplied by step.

s1 *= step;
s2 *= step;
s3 *= step;

The step adjustments vary the incrementation, and therefore the speed, of an rgb1rgb2 transition. Consider the first two fadeText( ) calls: the second call's "CCCCCC""000000" fade is twice as fast as the first call's "000000""CCCCCC" fade because the calls' step values are 4 and 2, respectively.

If an s1/s2/s3 value needs to go down and not up for the rgb1rgb2 transition, it is converted to its additive inverse.

if (r1 > r2) { s1 = -s1; }
if (g1 > g2) { s2 = -s2; }
if (b1 > b2) { s3 = -s3; }

We're almost ready to call the changeColor( ) function. The pre-fade increment data for the first fadeText( ) call's "000000""CCCCCC" transition is:

r1 = g1 = b1 = 0
r2 = g2 = b2 = 204
s2r = s2g = s2b = 0
s1 = s2 = s3 = 2 (204 → 1 → 2)
smallest = 204

We'll apply this data (well, some of it) to the str string in the following entry.

Comments: Post a Comment

<< Home

Powered by Blogger

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