momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Modelarea tablei şi jocului de şah (V)

CSS | FEN | jQuery | javaScript | sprite | widget
2012 jun

Am încheiat (IV) arătând o metodă obişnuită pentru a folosi imagini grafice ale pieselor. Anume, prevăzusem tabloul PIECES care asocia fiecărei piese-literă din FEN, <img>-ul care specifică fişierul imagine corespunzător - de exemplu PIECES['K'] = '<img src="images/20/wk.png" alt="K"/>'; iar în setPosition(), pentru piesa curentă din FEN se insera în câmpul corespunzător "<div class='piece'>" + PIECES[piece] + "</div>" - rezultând astfel diviziuni ca:

    <div class="field Row-1 Col-1 WhiteField">   <!-- câmpul curent -->
        <div class="piece">                      <!-- piesa plasată pe acesta -->
            <img alt="r" src="images/20/br.png"> <!-- încarcă imaginea piesei -->
        </div>
    </div>

Această soluţie are două defecte: browserul va trebui să descarce şi să păstreze (în cache) 12 fişiere ".png" (pentru fiecare set de piese); iar dacă am vrea să creem posibilitatea folosirii mai multor seturi de piese, atunci ar trebui să prevedem câte un tablou-asociativ PIECES_n pentru fiecare.

Deocamdată considerăm un singur set de piese - reprezentat ca şi mai înainte prin cele 12 fişiere ".png" din subdirectorul images/20 - şi vizăm aici o altă soluţie, bazată pe CSS-sprites.

Crearea şi folosirea unui sprite pentru un set de piese de şah

Un sprite combină mai multe imagini (de piese, aici) într-una singură; folosind proprietatea CSS background-position vom putea indica browserului ce parte din sprite (ce piesă) să poziţioneze într-un anumit container (pe câmpul respectiv al tablei de şah).

Un sprite se poate realiza şi manual - folosind de exemplu GIMP (de ce Photoshop? care este pentru Windows şi este Proprietary_software…). Dar este mult mai simplu să folosim o comandă de "montare" existentă în ImageMagick:

cd images/20 
montage -adjoin -tile 12x1 -geometry +0+0 -background none \
    wp.png wk.png wq.png wr.png wb.png wn.png \
    bn.png bb.png br.png bq.png bk.png bp.png \
    men20.png

Imaginea obţinută "men20.png" concatenează cele 12 piese pe un singur rând (în ordinea indicată), fară spaţiu liber între ele şi are background-ul transparent (încât culoarea aleasă pentru "câmp alb", respectiv "negru" nu va fi alterată prin plasarea piesei pe câmpul respectiv).

Iată de exemplu, cum putem selecta într-un <div> grupul ultimelor două piese din dreapta:

<div style="width: 40px; height: 20px;                border:1px dashed #666;
            background:url('images/20/men20.png');
            background-position: 40px;">
</div>

"men20.png" (indicat în background: url()) a fost "deplasat" cu 2x20 = 40 pixeli spre dreapta (conform background-position), rezultând astfel grupul ultimelor două piese. Acelaşi rezultat îl puteam obţine şi "deplasând" în sens negativ (spre stânga), punând "background-position: -200px;" (caz în care se maschează primele 10 piese, măsurând 10x20 = 200 pixeli).

Bineînţeles, se poate greşi: de exemplu, cu background-position: 50px, am obţine

Nu are nici o importanţă ordinea în care concatenăm piesele, dar o anumită simetrie este utilă de exemplu în cazul în care am seta valorile background-position printr-o funcţie javaScript; în alegerea făcută aici, dacă deplasăm spre stânga piesele albe şi spre dreapta pe cele negre, atunci suma dintre poziţia unei piese negre şi a celei albe de acelaşi nume este constantă (= 20px).

Pentru a exploata sprite-ul realizat, adăugăm în fişierul css/brw-sah.css următoarele definiţii:

.Piece { 
    width: 20px; height: 20px;
    background: url("../images/20/men20.png");
}
.white-pawn   {background-position: 0;}      .black-pawn   {background-position: 20px;}
.white-king   {background-position: -20px;}  .black-king   {background-position: 40px;}
.white-queen  {background-position: -40px;}  .black-queen  {background-position: 60px;}
.white-rook   {background-position: -60px;}  .black-rook   {background-position: 80px;}
.white-bishop {background-position: -80px;}  .black-bishop {background-position: 100px;}
.white-knight {background-position: -100px;} .black-knight {background-position: 120px;}

urmând ca setPosition() să insereze piesele sub forma <div class="Piece black-rook"></div> (unde .Piece indică url-ul imaginii, iar .black-rook indică poziţia turnului negru în cadrul sprite-ului).

În fişierul js/brw-sah.js înlocuim variabila PIECES cu:

    var PIECE_CLASS_NAMES = {
        p: "pawn", n: "knight", b: "bishop", r: "rook", q: "queen", k: "king"
    };

şi apoi rescriem metoda _setPosition() astfel:

    _setPosition: function(fen) {
        /* elimină '/' şi înlocuieşte fiecare cifră din FEN
           cu un număr corespunzător de spaţii */
        var fen64 = fen.replace(/\x2f/g, "")
                       .replace(/[1-8]/g, function(nr_sp) { 
                                  return "        ".substr(0, nr_sp); 
                                });
        /* parcurge în acelaşi sens cele 64 <div> şi FEN-ul
           înscriind piesa din FEN, sau ştergând vechea "piesă" */
        this.Tabla.children().each(function(index) {
            var piece = fen64.charAt(index);
            if (piece && piece != " ") {
                var piece_low = piece.toLowerCase(),
                    side = piece == piece_low ? "black" : "white";  
                    pieceClass = "Piece " + 
                                 side + "-" + PIECE_CLASS_NAMES[piece_low];
                $(this).html("<div class='" + pieceClass + "'></div>");
            }
            else $(this).html(""); // şterge piesa veche, dacă există 
        });
    }

După aceste modificări, încărcând în browser fişierul HTML (vezi (IV)) obţinem exact acelaşi rezultat ca în finalul din (IV); dar acum, câmpurile tablei (înscrise de setPosition()) apar ca:

    <div class="field Row-1 Col-1 WhiteField">
        <div class="Piece black-rook"></div>
    </div>

Mai târziu, vom reuşi să înlocuim - în cele 12 definiţii CSS ale poziţiilor pieselor (în .white-pawn, etc.) - măsura în pixeli cu una în procente, putând utiliza astfel aceleaşi 12 definiţii de poziţie pentru oricare set de piese (nu numai pentru setul 20x20, ca mai sus). Ceea ce va trebui schimbat de la un set la altul va fi doar definiţia pentru .Piece (precizând url-ul sprite-ului corespunzător acelui set, precum şi dimensiunile piesei).

vezi Cărţile mele (de programare)

docerpro | Prev | Next