Sunday, October 4, 2009

Creating a 7 Segment display using Javascript and CSS

Ok. This is how it started - One day I thought of writing a game completely in Javascript. I wanted it to look somewhat.. u know.. nice. So I decided to have a LED like thing to show scores (In the end, the game was good, but the LED was better). Anyway.. Lets get started

Creating HTML table for the 7 Segment display

First, lets have our CSS ready for the 7 segment:
table.7seg{
background-color:#000000;
vertical-align: top;
}

td.on{
background-color:#ffff00;
text-align: center;
vertical-align: top;
}

td.off{
background-color:#000000;
text-align: center;
vertical-align: top;
}
Here, we are making the table background color as black, and creating two styles - 'on' and 'off' for td elements. Background color is yellow for 'on' class, and black for 'off' class.
Then, we'll create a table in HTML. This table will have 3 columns and 5 rows :
Note that the height of 1st, 3rd and 5th rows is 5px, whereas, the height of 2nd and 4th rows is 40px. Similarly, the width of 1st and 3rd cells is 5px, whereas, width of 2nd cell is 40px.

<table align='center' width="50px" class="7seg">
<tr height="5px">
<td class="off" width="5px"></td>
<td id="seg_a" class="off" width="40px"></td>
<td class="off" width="5px"></td>
</tr>

<tr height="40px">
<td id="seg_f" class="off" width="5px"></td>
<td class="off" width="40px"></td>
<td id="seg_b" class="off" width="5px"></td>
</tr>

<tr height="5px">
<td class="off" width="5px"></td>
<td id="seg_g" class="off" width="40px"></td>
<td class="off" width="px"></td>
</tr>

<tr height="40px">
<td id="seg_e" class="off" width="5px"></td>
<td class="off" width="40px"></td>
<td id="seg_c" class="off" width="5px"></td>
</tr>

<tr height="5px">
<td class="off" width="5px"></td>
<td id="seg_d" class="off" width="40px"></td>
<td class="off" width="5px"></td>
</tr>
</table>

The 7 segment cells are given ids 'seg_a', 'seg_b', and so on, upto 'seg_g'
The naming these 7 segments is done according to this link.
CSS and HTML part is done. Now we have to build a logic to turn individual segments 'on' or 'off' for a given number. Since the 'on' or 'off' state of an individual segment is always constant for a given digit, we can use an associative array to maintain segment state for each digit.

First, we need an array to refer to the 7 segments:

var segList = new Array('a','b','c','d','e','f','g');
Next, we need a two-dimensional array of size 7*10 - 10 digits and state info (on/off) for each of the 7 segments:

var numMap = new Array(10);
for(var i=0; i<10; i++){
numMap[i] = new Array(7);
}


Now, we'll write state info for number 0.

numMap[0]['a'] = 'on';
numMap[0]['b'] = 'on';
numMap[0]['c'] = 'on';
numMap[0]['d'] = 'on';
numMap[0]['e'] = 'on';
numMap[0]['f'] = 'on';
numMap[0]['g'] = 'off';
Note that for 0, only segment 'g' is off.

Similarly, lets write for all numbers:
numMap[0]['a'] = 'on';
numMap[0]['b'] = 'on';
numMap[0]['c'] = 'on';
numMap[0]['d'] = 'on';
numMap[0]['e'] = 'on';
numMap[0]['f'] = 'on';
numMap[0]['g'] = 'off';

numMap[1]['a'] = 'off';
numMap[1]['b'] = 'on';
numMap[1]['c'] = 'on';
numMap[1]['d'] = 'off';
numMap[1]['e'] = 'off';
numMap[1]['f'] = 'off';
numMap[1]['g'] = 'off';

numMap[2]['a'] = 'on';
numMap[2]['b'] = 'on';
numMap[2]['c'] = 'off';
numMap[2]['d'] = 'on';
numMap[2]['e'] = 'on';
numMap[2]['f'] = 'off';
numMap[2]['g'] = 'on';

numMap[3]['a'] = 'on';
numMap[3]['b'] = 'on';
numMap[3]['c'] = 'on';
numMap[3]['d'] = 'on';
numMap[3]['e'] = 'off';
numMap[3]['f'] = 'off';
numMap[3]['g'] = 'on';

numMap[4]['a'] = 'off';
numMap[4]['b'] = 'on';
numMap[4]['c'] = 'on';
numMap[4]['d'] = 'off';
numMap[4]['e'] = 'off';
numMap[4]['f'] = 'on';
numMap[4]['g'] = 'on';

numMap[5]['a'] = 'on';
numMap[5]['b'] = 'off';
numMap[5]['c'] = 'on';
numMap[5]['d'] = 'on';
numMap[5]['e'] = 'off';
numMap[5]['f'] = 'on';
numMap[5]['g'] = 'on';

numMap[6]['a'] = 'on';
numMap[6]['b'] = 'off';
numMap[6]['c'] = 'on';
numMap[6]['d'] = 'on';
numMap[6]['e'] = 'on';
numMap[6]['f'] = 'on';
numMap[6]['g'] = 'on';

numMap[7]['a'] = 'on';
numMap[7]['b'] = 'on';
numMap[7]['c'] = 'on';
numMap[7]['d'] = 'off';
numMap[7]['e'] = 'off';
numMap[7]['f'] = 'off';
numMap[7]['g'] = 'off';

numMap[8]['a'] = 'on';
numMap[8]['b'] = 'on';
numMap[8]['c'] = 'on';
numMap[8]['d'] = 'on';
numMap[8]['e'] = 'on';
numMap[8]['f'] = 'on';
numMap[8]['g'] = 'on';

numMap[9]['a'] = 'on';
numMap[9]['b'] = 'on';
numMap[9]['c'] = 'on';
numMap[9]['d'] = 'on';
numMap[9]['e'] = 'off';
numMap[9]['f'] = 'on';
numMap[9]['g'] = 'on';
When the state info ready, rendering is fairly simple.
All we need to do is write a function, passing the number, and the segment id prefix ('seg' in this case).
And we're done. To test, create an input box and a button. All you have to do is pass the value in the input box to the display function, along with the segment prefix('seg').

function display(num,segName){
if(num){
num = parseInt(num);
for(var i=0; i<segList.length; i++){
var segment = segList[i];
document.getElementById(segName+"_"+segment).className = numMap[num][segment];
}
}
}

To see how it is implemented in my game, visit this link. Feel free to play.