⬆️ ⬇️

Electronic scoreboard 2 or for the benefit of society

Recently, there are not many good articles about web development on Habré. But now is not about that.

Sometimes habralyudi share interesting ideas, but do not reveal their essence. And maybe nothing. Because during the writing of the article (description) you notice what you have not seen before, other moves and decisions, and it is easier for the reader to understand the idea and give practical advice.

So a few days ago on the Habré appeared the article "Electronic scoreboard" , in which the author shared a link to his craft, but because of the insufficient description, the article came out of the category - "look what I did."

How interesting could the article be if the author added more descriptions. Therefore, I wanted to show with the example of this article how it would be possible to do a little better, and at the same time to share my implementation of his task.



I would like to note that I don’t want to say anything bad about the author (only that I didn’t add enough accompanying text). It can be seen that this work was “made with love”: many hours were spent on inventing, implementing and debugging. It turned out what happened. Not very simple, not very fast, but the main thing - it works.



What can we improve?

The first thing that catches your eye is the image for the light bulbs. I have nothing against - pretty lights. But if the picture is not loaded (at some point or at all), or the images are disabled (yes, this happens), then the user will not see anything. Not very nice. Therefore, you should always think about how we can get out of the situation in the absence of images, there should be some backup option.

Fortunately, in this case, this solution asks for itself and consists in changing the image:



(image magnified 16 times, checkered and translucent areas are marked)

A couple of minutes in Photoshop and we have a new image obtained from the original. If before that we had a white ball on a transparent substrate, now the square is filled with the background color (the color is left to be the author, that is, # 3d3d3d) with a hole (with a hole) in it. Before that, we were tied to the color of the light bulb (white) and the background could be any - now the opposite. Thus, we can use any color for the bulb, but this is not the main thing. The main gain is that in the absence of an image we will see a white (or other color) square, which will signal that the light is on. When the images become available, the bulb will be perfectly round, as it was before. With CSS we prescribe:

background: white url (dot.png);


And we get the following:



(on top of what will be without pictures, below - with pictures)

Here I also had to add an indent of 1 pixel between the blocks (light bulbs) so that they would not merge. But you can without it.



The next improvement is the adjustment of static HTML. Apparently the author does not really understand what and when it is better to use: classes or IDs, which tags in which situation. Although I still do not know myself :) I also admit that this code was “torn out” from somewhere, and it just has a heavy inheritance. Anyway, it seemed to me that HTML can be slightly simplified - throwing away all unnecessary, especially since it will be used for demonstration, that is, as an example.

A few thoughts. Using a list to enumerate form fields inside a <form> is somewhat strange, where it is more appropriate to use <div class = "field"> .. </ div>, for example. If you use a <label>, it is better to put <input> in its contents, or use the for attribute. That is, instead of:

< label > E-TABLO HORIZ. POINTS: </ label >

< input type ="text" name ="w" value ="55" />




* This source code was highlighted with Source Code Highlighter .


much better:

< label for ="w" > E-TABLO HORIZ. POINTS: </ label >

< input type ="text" name ="w" value ="55" />




* This source code was highlighted with Source Code Highlighter .


or that I prefer more:

< label >

< span class ="title" > E-TABLO HORIZ. POINTS: </ span >

< input type ="text" name ="w" value ="55" />

</ label >




* This source code was highlighted with Source Code Highlighter .


This is all the more relevant in the strategy by which the author made the layout: set for <label> display: block & float: left . To make it easier to manipulate (to achieve visual effects, positioning), the latter option is best suited.

Another practical advice is not to give short IDs, classes, names to fields like “w”, as well as variables with a large field of view, especially if you want to give someone else (or someone who will continue to support it) to understand the code. This may have a bad effect in the future or on a big project (even if it will never be born). It would be much clearer if instead of “w” it would be written “tabloWidth”, “tablo_width” or, at worst, just “width”.

But this is all no more than another opinion. In this case, mine.

')

Separately, I want to note the styles * {outline: none; } and div {display: none! important; } - just a great "present" for those who want to figure out how it all works in firebug. At first it seemed to me that I had something wrong with firebug - it turned out to be much easier. If someone doesn’t understand what I’m talking about, these styles “turn off” the highlight of the blocks when you hover over them when you want to select an element on the page. It seems to me, if you lay out an example on habr with such styles, then this is just a mockery of mere mortals. Surprisingly, javascript without obfuscation ...



Well, now directly javascript itself and the algorithm.

Studying the code, I confess I am confused. Somehow everything is complicated, and really too much code. There are guesses how this works, but I could be wrong, so I won’t guess and tell you how I would do it (or rather did it).

The first thing you wanted to change is the symbol table.

Of course, alternative solutions emerged in the discussion of the article, when you can do without a symbol table (in which, in essence, the font is coded). The essence of the method is to use some kind of grid (that is, an image with holes), under which to “output” plain text in regular font, and then move it. This is sometimes done in various graphical environments and games. But in this case, the grain (the size of the holes) should be small, ideally 1 pixel, otherwise (as in ours, with a hole size of ~ 10 pixels), it is easy to get a “missing pixels” situation, that is, when our hole is only part of the letter is visible. And even if it turns out on a certain browser to achieve a clear hit in the pixels in some kind of font, then to achieve this everywhere and always, IMHO, is impossible.

But back to the table. An image is encoded for each character using an array of zeros and ones. For example, the letter "T":



I think everything can be understood from the picture. The coding method is in principle normal, since it is convenient to “draw”. But too redundant and inconvenient to use.

Let us re-encode characters so that the character is encoded not by line, but by vertical lines. And the zeros and ones put bits in a number. For this we use a small converter:

var letters = {

//

};

//

var letters2 = {};

//

for ( var letter in letters)

{

// - ( 8 )

var lineCount = letters[letter].length / 8;

// ( )

var converted = [];

//

for ( var i = 0; i < lineCount; i++)

{

var line = 0;

// ""

for ( var j = 0; j < 8; j++)

// ... , :)

// : ""

line = line | letters[letter][j * lineCount + i] << j;



converted.push(line);

}

//

letters2[letter] = converted;

}

// " "

for ( var letter in letters2)

{

document .write( "<br>'" + letter + "': [" + letters2[letter] + "]," );

}



* This source code was highlighted with Source Code Highlighter .




After such a conversion, our letter “T” in the code will look like this:

'T': [3, 3, 255, 255, 3, 3]


What is much more compact (the table itself has decreased by 3 times), but this is not an end in itself. In this scenario, it has become more difficult to encode new characters (on the other hand, you can encode as before, and then use the converter), but it is much more convenient to use in the code (now you will see it).



Now that we've converted the symbol table, it's time to move on to the main part. First, you need to decide on the canvas (the next I will call it a conveyor, since this name is more appropriate for the logic of the work). It seems logical to form the entire strip from the text right away, and not to do it at each step of the animation - during the animation we will simply shift. At the current state of affairs, it is very simple to form such a band and it will be a linear (one dimension) array containing numbers. This is done by the following function:

//

var pipeline = [];



//

function fill(str){

//

pipeline.length = 0;



// ,

//

str = (str + ' ' ).toUpperCase();



//

for ( var i = 0; i < str.length; i++)

// ,

if (letters[str.charAt(i)])

{

// ,

// ,

pipeline.push.apply(pipeline, letters[str.charAt(i)]);

pipeline.push(0);

}



// , ( )

while (pipeline.length <= tabloWidth)

pipeline.push(0);

}



* This source code was highlighted with Source Code Highlighter .




It seems so far simple. When calling, say, fill ('habrahabr'), the pipeline array will contain:

[255, 255, 24, 24, 255, 255, 0, 252, 254, 51, 51, 254, 252, 0, 255, 255, 153, 153, 255, 118, 0, 255, 255, 27, 59 , 255, 206, 0, 252, 254, 51, 51, 254, 252, 0, 255, 255, 24, 24, 255, 255, 0, 252, 254, 51, 51, 254, 252, 0, 255 , 255, 153, 153, 255, 118, 0, 255, 255, 27, 59, 255, 206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


Before proceeding to the interpretation of this array, we need to create an HTML structure of the scoreboard. Since it may have different widths, for this purpose we will write a function. The size of the scoreboard is set by two variables - tabloWidth and tabloHeight, respectively. In our case, tableHeight does not change and is always 8.

The author of the article, based on which this one was written, used DOM methods for generating a structure, which is good and for which he can shake a paw. However, he simply added tabloWidth x tabloHeight elements to the container, and they were broken into lines because the container was limited in width. For this, he calculated and set the container a new width (in pixels) when changing tabloWidth . This is not very convenient, since the size of the light bulb turns out to be “wired” in the code, and when the CSS (light bulb size) is changed, we will have to edit the code.

I acted differently. In the container I place <div> elements, the number of which is equal to the number of lines, and in each of them there is already the necessary number of <span> (light bulbs). It is more convenient both for layout and for further manipulations (animation). Also, in this case, we can create only one line and then clone it tableHeight - 1 time, which will turn out much faster (although the gain, of course, is doubtful here). By default, we do not put any classes on the elements (this corresponds to the fact that the light bulb is on), and later we will add the class off for off lights. So, the text of the function:

// HTML

function createTablo(){

//

while (htmlTablo.lastChild)

htmlTablo.removeChild(htmlTablo.lastChild);



//

var lineProto = document .createElement( 'DIV' );

for ( var i = 0; i < tabloWidth; i++)

lineProto.appendChild( document .createElement( 'SPAN' ));



//

htmlTablo.appendChild(lineProto); //

for ( var i = 0; i < tabloHeight - 1; i++)

//

htmlTablo.appendChild(lineProto.cloneNode( true ));

}



* This source code was highlighted with Source Code Highlighter .


The output in the container referenced by htmlTablo will be something like this HTML:

< div >< span ></ span >< span ></ span >< span ></ span > ... </ div >

< div >< span ></ span >< span ></ span >< span ></ span > ... </ div >

...

< div >< span ></ span >< span ></ span >< span ></ span > ... </ div >




* This source code was highlighted with Source Code Highlighter .




Now everything is ready to finally engage in animation, or rather, in the animation step, that is, the shift. The next function performs this task, namely, it lights and extinguishes the bulbs, and shifts everything one column to the left. The cursor variable points to the line (column) that will be currently displayed on the right. After the line is “drawn out”, the cursor increases by one (moves to the next element-line), and when it reaches the end of the array (pipeline, where all the lines are stored) goes back to the beginning (becomes zero). That is, the animation is looped. The code itself:

//

function step(){

//

for ( var i = 0, line = htmlTablo.firstChild; line; i++, line = line.nextSibling)

{

//

var dot = line.firstChild;



//

line.appendChild(dot);



//

// :) ""

// ( ), - off

dot.className = (pipeline[cursor] >> i) & 1 ? '' : 'off' ;



// :

// var dot = line.lastChild;

// line.insertBefore(dot, line.firstChild);

// dot.className = (pipeline[pipeline.length - cursor - 1] >> i) & 1 ? '' : 'off';

}

// ""

cursor = (cursor + 1) % pipeline.length;

}



* This source code was highlighted with Source Code Highlighter .


A little about the method of displacement. We do not repaint all the light bulbs, but simply take the first column and put it to the end. For this we use the DOM. Then we repaint the bulbs (change the class of <span>) of the last column (which was the first before) in accordance with the next value in the pipeline array. This is much cheaper than changing the class for all <span>: instead of changing the class for tabloWidth x tabloHeight elements, we only change for tabloHeight .

The comments also provide the code for the shift to the right (only this may not be enough, you may need to change something in other places).



That's all. Only initialization remains: we need to create a scoreboard ( createTablo function), fill the pipeline with text ( fill function), execute the tableWidth step-shift times (that is, the step function) in order to clear the scoreboard and fill it with the initial state, and set up a call using setInterval step functions with the required interval. The text of the function that does all this can be viewed in the working example, the title is left to be the author - ENJOY, so as not to spoil the celebration of life :)

View result



What we have achieved:

* More simple and short code;

* It seems to work faster;

* Full control on the style through CSS, that is, no size is not "sewn up" in the code; In addition, you can change the color of light bulbs, for example, for extinguished light bulbs, I set the color a bit darker than the background, which added more realism, and the extinguished light bulbs also stand out from the background; you can experiment and make, for example, a rainbow on the board;

* Not tied to the images, the user will be able to see something without them;

* Revealed, explained how it works :)

And the code turned out to be three times smaller than the original one (if you skip both sources, for example, through the packer , which will remove spaces / comments, and compare). How much easier - to judge you.

Of course, the animation script is a little different from the original one, but, I suppose, if you wish, you yourself can achieve the desired result - you have the foundation.



PS

I would like to see articles about this form. I decided not just to say it, but to do it. Maybe my writing style is not the best and in some places too detailed. I tried to make this article useful, and it was easier for beginners to understand. Writing a working example took much less time than simplifying the code, writing comments and this article (which suffered for three days due to lack of time). I hope someone will come in handy.

We are waiting for good articles on Habré.



ENJOY!



UPDATE: Some, having read to the end, forget (or do not pay attention) that I am not the author of the original idea, the author of which is regeda , which he wrote about in his article The Electronic Scoreboard . I am just the author of another implementation of this article.

Also this article is intended only to demonstrate the approaches and ideas, and the board itself is just an example. Remember, as in the Matrix: I can only show the door, but you have to enter it yourself "...

Source: https://habr.com/ru/post/64381/



All Articles