Mouse Trails

Richard A. DeVenezia, August 2003

The Example

The user roves their mouse over a table rendered in the appearance of a grid. When the mouse moves over a cell its background changes and when the mouse moves out of a cell its background fades out.

Show me the grid

Details of html

The javascript is stored in an external file and is referenced in the HEAD.

<SCRIPT LANGUAGE="JAVASCRIPT" SRC="mouse-trail.js">

The table appearance is made grid-like by specifying css in the HEAD.

<STYLE TYPE="text/css">
  table#grid { border-collapse: collapse }
  table#grid tr td { width:15px; height:15px; border: 1px solid #E8E8E8 }
</STYLE>

The number of rows and columns of the grid can be altered entering values in a form and clicking a button.

<FORM NAME="GridInput" ONSUBMIT="return false">
  <TABLE BORDER="0">
    <TR><TD>ROWS</TD>
        <TD><INPUT NAME="NROWS" VALUE="20" SIZE="3"></TD></TR>
    <TR><TD>COLS</TD>
        <TD><INPUT NAME="NCOLS" VALUE="20" SIZE="3"></TD></TR>
    <TR><TD COLSPAN="2">
        <INPUT TYPE="BUTTON"
               VALUE="Make Grid"
               ONCLICK="makeGridSubmit()">
        </TD></TR>
  </TABLE>
</FORM>

An empty table identified as GRID is placed below the form. This is the table the javascript operates on.

<TABLE ID="GRID" ><TR><TD></TD></TR></TABLE>

Beneath the table a javascript call is used to create the initial grid. The same call could be made by BODY onLoad.

<SCRIPT LANGUAGE="JAVASCRIPT">
makeGridSubmit()
</SCRIPT> 

Outline of processing

Each cell the mouse moves over has its background changed to a starting color. When the mouse moves away from a cell, setTimeout is used to run a transition function. The transition function runs itself again, using setTimeout, if the cell has not reached its final background color. If a cell is transitioning and the mouse moves over it again, the transitioning restarts.

For fastest processing an array is used to store object references to each cell of the table.

All color values must be of the form #rrggbb.

Details of javascript

makeGridSubmit() obtains the number of rows and columns from the html form. Checks are performed to ensure the grid is not too large.

//--------------------------------------------------------------
function makeGridSubmit () {
  if (document.GridInput.NROWS.value > 40) document.GridInput.NROWS.value = 40
  if (document.GridInput.NCOLS.value > 40) document.GridInput.NCOLS.value = 40

  makeGrid ( document.GridInput.NROWS.value
           , document.GridInput.NCOLS.value
           )
  return false
}

The table that is the grid is created by the makeGrid() function. The function relies on a table identified as GRID pre-existing in the html. Each cell of the table is given three new properties; index, step and timerId. Event handlers for onMouseOver and onMouseOut are installed. A reference to each cell is stored in the global array cells.

step records the current state of transition and is the number of steps away from completion.
Tracking the current timerId is required to handle the situation when a transitioning cell is moved over again, at which point transitioning needs to be stopped.

var cells = new Array()
//--------------------------------------------------------------
function makeGrid (nRows, nCols) {
  table = document.getElementById ('GRID')

  for (var i=0; i < table.rows.length; ) {
    table.deleteRow(0)
  }

  cells = new Array()

  for (var i=0; i<nRows; i++) {
    newrow = table.insertRow (i)
    for (var j=0; j< nCols; j++) {
      newcell = newrow.insertCell (j)

      newcell.index = i * nCols + j
      newcell.step = 0
      newcell.timerId = 0

      newcell.onmouseover = function (evt) { over (this) }
      newcell.onmouseout  = function (evt) { out  (this) }

      cells [ newcell.index ] = newcell
    }
  }

  return false // prevent page reload
}

Big deal, so now we have a table. What's next ?

Let's examine the event handlers and parameters that control the fadeout effect. When the mouse moves over a cell the background is set to overColor. When the mouse moves away from a cell the background needs to transition toward the page background color, outColor. The transition must occur through a number of steps, each lasting stepDuration milli-seconds. The controlling parameters are global variables.

out() initiates the transition process by passing doTransition() its index. The index will be used to lookup the cells object reference in the cells array.

//--------------------------------------------------------------
// global variables

var overColor = '#7093DB'
var outColor  = '#FFFFFF'

var steps = 50
var stepDuration = 40

//--------------------------------------------------------------
function over (cell) {
  clearTimeout (cell.timerId)  // stop transitioning
  cell.step = steps
  cell.style.backgroundColor = overColor
}

//--------------------------------------------------------------
function out (cell) {
  setTimeout ("doTransition("+cell.index+")", 0)
}

What's left? The transitions! Each transition step of a cell is performed by doTransition(). The step counter is decremented and if any steps remain doTransition() is scheduled to run again. When the counter reaches zero, the transition is complete. Linear interpolation is used to compute the r, g, b component values.

//--------------------------------------------------------------
function doTransition (index) {
  var cell = cells [ index ]

  if (cell.step) {
    cell.step --

    var from = overColor.substr(1)
    var   to = outColor.substr(1)
    var step = cell.step

    var r0 = parseInt (from.substr(0,2),16)
    var g0 = parseInt (from.substr(2,2),16)
    var b0 = parseInt (from.substr(4,2),16)

    var r1 = parseInt (  to.substr(0,2),16)
    var g1 = parseInt (  to.substr(2,2),16)
    var b1 = parseInt (  to.substr(4,2),16)

    var r = Math.floor (r1 * ((steps-step)/steps) + r0 * (step/steps))
    var g = Math.floor (g1 * ((steps-step)/steps) + g0 * (step/steps))
    var b = Math.floor (b1 * ((steps-step)/steps) + b0 * (step/steps))

    var color = htmlColorString (r,g,b)

    cell.style.backgroundColor = color
  }

  if ( cell.step != 0 )
    cell.timerId = setTimeout ("doTransition("+index+")", stepDuration)
}

Lastly, the htmlColorString snippet

//--------------------------------------------------------------
function htmlColorString (r,g,b) {
  var r = r.toString(16); if (r.length == 1) r = '0'+r;
  var g = g.toString(16); if (g.length == 1) g = '0'+g;
  var b = b.toString(16); if (b.length == 1) b = '0'+b;

  return "#" + r + g + b
}

Your challenge

Modify the JavaScript to handle the situation of 'fast mousing.' If the mouse moves to quickly, then the event handlers will fire on cells that are not adjacent. If that is the case, use linear interpolation to start the fade effect for the cells between the current cell and prior cell.


ROWS
COLS

The Grid

Comments or questions ? Contact Richard A. DeVenezia