reptile7's JavaScript blog
Thursday, October 09, 2014
 
Δcolor/Δt
Blog Entry #337

Welcome back to our discourse on the Java Goodies Text Fader 1.3 script. Today we'll take on the script's changeColor( ) function, which
(1) loads the text+HTML str string into the fadeMe01 or fadeMe02 div and
(2) changes the str text's color from rgb1 to rgb2.

The changeColor( ) function is set up like a

for (var counter = 0; counter < smallest; counter++) { ...statements... }

loop in that it runs for smallest iterations according to a counter < smallest condition. The counter variable is initialized to 0 when changeColor( ) is called and is incremented by a counter++ expression near the end of the function. The smallest limit is set by a

smallest = Math.round(smallest / step);

command that divides the smallest color distance by the step gradient, round( )s the quotient, and assigns the resulting integer back to smallest. The smallest redefinition concludes the with block in the fadeText( ) function although you can move it to the beginning of the changeColor( ) function if you want to.

When the fadeText( ) with statement has finished executing, control passes to an if...else statement whose if clause triggers the changeColor( ) functionality. Non-IE modern browsers won't go through the both gate; of course, we would really prefer to not condition the changeColor( ) call at all.

if (both) { changeColor(obj, str, rgb1, speed, 0); }
else { document.write("Error: For some reason you tried viewing this page with a browser that doesn't [have] JavaScript/CSS block support."); }


The changeColor( ) call passes five arguments to the changeColor( ) function. The obj, str, rgb1, and speed arguments are the same ones we passed to the fadeText( ) function in the previous post; arguments[4], 0, is assigned to the counter counter.

The aforementioned counter < smallest condition gatekeeps the changeColor( ) body.

function changeColor(obj, str, rgb1, speed, counter) { if (counter < smallest) { ... } }

The changeColor( ) function is recursive: the counter++ incrementation is followed by a setTimeout( ) command that calls changeColor( ) after a 10-millisecond delay.

/* timerID is declared globally just before the stopClock( ) function. */
var timerID = null;
...
timerID = window.setTimeout("changeColor('" + obj + "', '" + str + "', '" + rgb1 + "', " + speed + ", " + counter + ")", speed);


Use of a function pointer for the first setTimeout( ) argument allows us to formulate the command as:

timerID = window.setTimeout(function ( ) { changeColor(obj, str, rgb1, speed, counter); }, speed);
/* A lot cleaner, huh? */


Stopping the clock, or not

If counter is less than smallest, the changeColor( ) function first calls a stopClock( ) function.

var timerRunning = false;

function stopClock( ) { if (timerRunning) window.clearTimeout(timerID); timerRunning = false; }

The preceding code was lifted verbatim from this Netscape example (OK, Netscape names the function stopclock( ), but otherwise...), which uses it to make sure a clock script's clock is stopped before setting it in motion.

The recursive changeColor( ) call is followed by a timerRunning = true; assignment. In the counter = 0 changeColor( ) iteration the stopClock( ) function doesn't do anything (OK, it resets timerRunning to false) but in each subsequent iteration stopClock( ) clears the timerID timeout. We shouldn't need to do this, should we? The Netscape example runs the stopclock( ) function only once, when the page loads, as a housekeeping measure. In the event, commenting out the stopClock( ) call
(4) does give rise to some jittery movie behavior with IE 4.5 and Netscape 4.61 but
(5) has no effect with IE 5.1.7
in the SheepShaver environment.

The str string and its starting color(s)

Next, the obj div's link/text colors are set to rgb1 and the str string is loaded into the obj div by either of two if statements that respectively cater(ed) to Netscape and IE.

if (layers) with (document[obj]) { document.open( ); document.linkColor = rgb1; document.fgColor = rgb1; document.write(str); document.close( ); } if (style) { eval(obj + ".document.linkColor = rgb1"); eval(obj + ".innerHTML = '<font color=' + rgb1 + '>' + str + '<\/font>'"); }

On the Netscape side, an absolutely positioned div is a layer and thus has a document property that provides access to the various methods and properties of the document object. The fadeMe01/fadeMe02 layers are empty and we can use layerObject.document commands to kit them out as we see fit.

On the IE side, the innerHTML command is straightforward enough but the eval(obj + ".document.linkColor = rgb1"); command for setting the color of unvisited hyperlinks initially threw me for a loop: I knew that obj could serve as an object reference for the fadeMe01|fadeMe02 div, but what's with the .document business? A for...in probe of obj (more specifically, of eval(obj) - obj itself is just a string)

var result = "";
for (var i in eval(obj)) result += "obj." + i + " = " + eval(obj)[i] + "\n";
window.alert(result);


revealed that, yes, the IE-interpreted obj object does in fact have an obj.document = [object document] property à la a Netscape layer - you learn something new every day.

In recasting the IE code for modern browsers, I found that

document.getElementById(obj).innerHTML = str;
document.getElementById(obj).style.color = "#" + rgb1;


would color nonlink text but not link text, which would render in white per the a:link { color:white; } style rule and then stay white and not fade; adding

var divLinks = document.getElementById(obj).getElementsByTagName("a");
for (i = 0; i < divLinks.length; i++) divLinks[i].style.color = "#" + rgb1;


solved this problem.

Increment management

With the str text color now rgb1, we're ready to start our rgb1rgb2 journey. Returning to the s1/s2/s3/s2r/s2g/s2b decimal data generated by the fadeText( ) function, we first increment s2r, s2g, and s2b by s1, s2, and s3, respectively.

s2r += s1;
s2g += s2;
s2b += s3;


As repositories for rgb1's decimal R/G/B components, the s2r/s2g/s2b values are initially integers. For some color transitions, however, one or more of the s1/s2/s3 values can be floating-point numbers, in which case the corresponding incremented s2r/s2g/s2b values will be floating-point numbers as well. The new s2r/s2g/s2b values are round( )ed (whether they need it or not) as they will shortly be mapped onto their hexadecimal counterparts via the convert database, which requires integer inputs and returns undefined if it doesn't get them.

tempR = Math.round(s2r);
tempG = Math.round(s2g);
tempB = Math.round(s2b);


For the purpose of further incrementation in subsequent changeColor( ) iterations, however, floating-point s2r/s2g/s2b values should stay just as they are - the s1/s2/s3 values are meant to take us from rgb1 to rgb2 in exactly smallest iterations, and we won't hit the rgb2 target if s2r/s2g/s2b non-integers are 'smoothed out' - so the round( ) returns are assigned to a separate set of tempR, tempG, and tempB variables.

Depending on
(a) the rgb2 value,
(b) the step value, and
(c) whether the r1/g1/b1r2/g2/b2 color distances are or are not evenly divisible by step,
it is possible for one or more of the tempR/tempG/tempB values to stray outside the 0-255 range at the end of a fading operation. If any of the tempR/tempG/tempB values hit 0 or go below 0, they are set to 0; if any of them hit 255 or go above 255, they are set to 255.

if (tempR <= 0) tempR = 0; /* <= can be replaced by < */
if (tempG <= 0) tempG = 0;
if (tempB <= 0) tempB = 0;
if (tempR >= 255) tempR = 255; /* >= can be replaced by > */
if (tempG >= 255) tempG = 255;
if (tempB >= 255) tempB = 255;


We're getting there. In preparation for the next changeColor( ) iteration, the tempR/tempG/tempB values are plugged into the convert array and the resulting two-digit hexadecimal number strings are concatenated to give a new six-digit RRGGBB value, to which rgb1 is set.

rgb1 = convert[tempR] + convert[tempG] + convert[tempB];

Is it necessary to hexadecimalize the tempR/tempG/tempB values? Nope. Upon adding a

tempR = s2r, tempG = s2g, tempB = s2b;

line to the end of the fadeText( ) with block, we can color the str text with the decimal tempR/tempG/tempB values via an rgb( ) function.

document.getElementById(obj).style.color = "rgb(" + tempR + ", " + tempG + ", " + tempB + ")";
var divLinks = document.getElementById(obj).getElementsByTagName("a");
for (i = 0; i < divLinks.length; i++) divLinks[i].style.color = "rgb(" + tempR + ", " + tempG + ", " + tempB + ")";


Upon resetting rgb1, the changeColor( ) function concludes by incrementing counter, calling itself, and toggling timerRunning to true as described earlier.

In the following entry, we'll recap vis-à-vis the first four fadeText( ) calls, go through the rest of the Text Fader 1.3 movie, refine the code a bit more, and maybe even roll out a demo.

Comments: Post a Comment

<< Home

Powered by Blogger

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