momente şi schiţe de informatică şi matematică
anti point—and—click

Poziţionarea procentuală a unei piese dintr-un sprite

background-position | CSS | sprite
2012 jun

Pentru background-position: X% nu am găsit decât iterări (şi exemplificări vizuale imediate) ale formulării din CSS 2.1, conform căreia imaginea va fi poziţionată în câmpul-destinaţie în aşa fel încât punctul din imagine de coordonate (X%, 50%) să coincidă cu punctul care în cadrul câmpului-destinaţie are coordonatele (X%, 50%).

Vom folosi pentru investigările necesare următorul sprite:

conţinând cele 12 piese de şah, de dimensiune NxN pixeli; pentru lizibilitate, am inclus piesele propriu-zise în câte un cadru de 1 pixel, încât aici N este cu 2 pixeli mai mare decât dimensiunea reală a piesei (ceea ce nu ar trebui să aibă importanţă, dat fiind că vom opera cu procente).

Pe de altă parte, considerăm undeva într-o pagină HTML, un element <div> (sau mai multe), având fixată aceeaşi dimensiune ca şi piesele, NxN pixeli.

Poziţionările :0% şi respectiv :100% sunt clare: marginea stângă (cu abscisa 0%) şi respectiv cea din dreapta imaginii (cu abscisa 100%) este poziţionată peste marginea stângă şi respectiv peste cea dreaptă a <div>-ului, aşezând aici atâta cât încape din imagine (începând de la marginea stângă, respectiv până la cea dreaptă a imaginii).

Abscisa 50% a imaginii "cade" pe linia care separă caii şi acesteia îi corespunde în <div>-ul nostru linia verticală (imaginară) dusă prin mijlocul acestuia. Prin urmare, background-position: 50% va înscrie în <div> jumătăţi ale cailor (de fapt, nu chiar "jumătăţi" fiindcă intervine şi grosimea liniei separatoare - care există şi ea, în imagine):

<!DOCTYPE html>
<head>
    <style>
        .test {
            width: 34px; height: 34px; margin-right: 4px; float: left;
            background: lightgrey url("images/test-men32.png");
        }
    </style>
</head>
<body>
    <div class="test" style="background-position:50%"></div>
    <div class="test" style="background-position:0%"></div>
    <div class="test" style="background-position:100%"></div>
    <div style="clear:left;"></div>
</body>

Să vedem acum, cum am putea elimina jumătatea de cal negru apărută în <div> prin :50%, pentru a obţine în <div>-ul nostru calul alb "complet". Pentru ca în <div> să apară ambele jumătăţi ale calului alb, ar trebui să "suprapunem" verticala mediană a calului alb din cadrul imaginii, peste verticala mediană a <div>-ului.

Ce abscisă are verticala mediană a calului alb, în cadrul imaginii? Deoarece abscisa 50% indica marginea dreaptă a cadrului calului alb din imagine, rezultă că abscisa verticalei mediane a calului alb se obţine scăzând din 50% procentul corespunzător unei jumătăţi de piesă; o piesă din imagine are dimensiunea 100% / 128.3333%, deci abscisa căutată este 50% - 8.3333%/245.8333%.

Analog, adunând 8.3333% / 2 la 50%, obţinem abscisa care ar selecta calul negru: 54.1667%.

Nu-i sigur că este suficient de corect - şi n-ar fi vorba atât de erorile de aproximare, cât de faptul că totuşi, abscisei 45.8333% a imaginii nu-i corespunde chiar "verticala mediană" a <div>-ului, ci verticala de abscisă 45.8333% a <div>-ului! Dar adăugând aceste <div>-uri în fişierul HTML redat mai sus, constatăm că rezultatul este aproape mulţumitor:

    <div class="test" style="background-position:45.8333%"></div>
    <div class="test" style="background-position:54.1667%"></div>

Privind cu atenţie (se poate folosi eventual, "Zoom In" din meniul View al browserului) constatăm că în <div> calul alb este cu o nuanţă mai la stânga, iar cel negru este cu o nuanţă mai la dreapta faţă de caii corespunzători din sprite

Iar acel 50% de la care am scăzut sau am adunat este nu marginea dreaptă sau stângă a vreunuia din caii din sprite, ci este abscisa verticalei imaginare care desparte cadrul calului alb de cadrul celui negru (amintim că am "împrejmuit" fiecare piesă din sprite cu un cadru de 1 pixel) - prin urmare "nuanţa" observată mai sus ar fi chiar mai mare, dacă vom renunţa (cum este firesc) la cadrele acum existente, în jurul pieselor.

Altfel spus, am scăzut prea mult (în cazul calului alb) şi respectiv, am adunat prea puţin (în cazul celui negru) faţă de abscisa considerată iniţial de 50%.

Să analizăm şi cazul regelui alb (a doua piesă din sprite). Abscisa cadrului stâng al acestei piese este 8.3333% (am văzut mai sus că fiecare piesă, deci şi prima, are lăţimea de 8.3333% din lăţimea întregii imagini) şi acest cadru va fi poziţionat în <div> la 8.3333% (din lăţimea <div>-ului) distanţă de marginea stângă a <div>-ului.

Cât este 8.3333% din lăţimea <div>-ului? Este 8.3333% din {8.3333% din lăţimea imaginii} - fiindcă <div>-ul are aceeaşi lăţime ca piesele din sprite - adică este 8.3333 * 8.3333 / 100 ≅ 0.6944% din lăţimea imaginii. Adunând 0.6944% la abscisa cadrului stâng vizată mai sus, rezultă poziţionarea "exactă" : 9.0277% în <div> a regelui alb:

    <div class="test" style="background-position:8.3333%"></div>
    <div class="test" style="background-position:9.0277%"></div>

În general, dacă vizăm piesa de index K = 1 (regele alb) până la K = 10 (regele negru), abscisa Q a cadrului stâng a acesteia este Q = K * 8.3333% din lăţimea sprite-ului şi pentru poziţionare "exactă" în <div> trebuie să-i adunăm (K * 8.3333% din 8.3333%).

Poziţionarea nu este chiar "exactă", fiindcă am măsurat ceea ce trebuie adunat abscisei Q, de la cadrul stâng şi nu de la marginea stângă propriu-zisă a piesei (deci am adunat ceva mai puţin decât trebuie). Şi vedem aceasta luând K = 10 (vizând regele negru); în acest caz, Q = 83.3333% şi trebuie să-i adunăm 10*0.69444 rezultând poziţionarea : 90.2777%:

    <div class="test" style="background-position:90.2777%"></div>

Am mărit imaginea ca să vedem că încă n-am reuşit să scoatem complet, cadrul stâng. Noi vrem să plasăm în <div> piesa, dar am "măsurat" de la cadrul (inclusiv) care împrejmuieşte piesa (nu de la marginea-stângă propriu-zisă a piesei). Ca să eliminăm complet cadrul stâng, ar trebui să vedem ce procent din imagine ocupă un cadru, să raportăm acest procent la lăţimea <div>-ului şi să adunăm rezultatul (multiplicat cu factorul K) lui Q…

Nu mai redăm aici acest calcul (sincer, nici nu l-am mai făcut!) pentru că între timp, am mai experimentat o idee: cum stau lucrurile dacă folosim procente negative?

Bineînţeles, am început cu : -50%… dar apoi am avut inspiraţia de a proba : -100%, apoi : -200%, : -300% ş.a.m.d. - constatând neaşteptat, că aceste poziţionări rezolvă perfect problema de a obţine în <div> exact piesa dorită!

<div class="test" style="background-position:-50%"></div>

<div class="test" style="background-position:-100%"></div>  <!-- a doua piesă -->
<div class="test" style="background-position:-200%"></div>  <!-- a treia -->
<div class="test" style="background-position:-300%"></div>
<div class="test" style="background-position:-400%"></div>
<div class="test" style="background-position:-500%"></div>
<div class="test" style="background-position:-600%"></div>
<div class="test" style="background-position:-700%"></div>
<div class="test" style="background-position:-800%"></div>
<div class="test" style="background-position:-900%"></div>
<div class="test" style="background-position:-1000%"></div> <!-- penultima piesă -->

<div class="test" style="background-position:0%"></div>    <!-- Prima piesă -->
<div class="test" style="background-position:100%"></div>  <!-- Ultima piesă -->

Cu "Zoom In" vedem că fiecare <div> conţine nu numai piesa, dar şi cadrul de 1 pixel cu care am împrejmuit-o la construcţia sprite-ului (o ultimă dovadă că piesa este poziţionată "exact").

În final, putem formula mai general: dacă imaginile care compun un sprite au toate aceeaşi lăţime, atunci poziţionarea imaginii de index K într-un <div> de aceeaşi lăţime se face prin background-position: -K*100% (cu procent negativ).

Explicaţia poziţionării unei imagini prin -K*100%

Cum se explică acum, ceea ce am găsit mai sus graţie unui moment de inspiraţie? Cu alte cuvinte - cum se aplică regula generală "imaginea va fi poziţionată în câmpul-destinaţie în aşa fel încât punctul din imagine de coordonate (X%, 50%) să coincidă cu punctul care în cadrul câmpului-destinaţie are coordonatele (X%, 50%)" în cazul procentelor negative?

În cadrul imaginii, poziţionarea negativă (fie în procente, fie în pixeli) se calculează de la dreapta spre stânga imaginii (invers faţă de poziţionarea "pozitivă").

De exemplu, : -100% stabileşte ca origine marginea stângă a imaginii; dar… la fel şi pentru -200%, sau -300%, etc.! Poziţionarea : -K*100% stabileşte originea la marginea stângă a imaginii, indiferent cât este factorul K.

Putem zice că : -K*100% "nu are sens" referitor la imagine - va fixa originea imaginii la fel ca şi ": -100%". Dar regula evocată la început aplică ": -K*100" şi <div>-ului în care vrem să poziţionăm o porţiune din imagine, începând de la originea fixată acesteia (şi astfel, K "are sens").

Totdeauna poziţionarea în <div> (fie pozitivă, fie procentual negativă) se face relativ la marginea stângă a acestuia. Ca urmare, o abscisă procentuală negativă pointează în stânga <div>-ului (înafara lui, spre stânga).

Deci "-100%" de exemplu, înseamnă pentru <div> "deplasarea" fictivă a marginii stângi a sale spre stânga, pe o distanţă egală cu lăţimea lui ("creând" o zonă fictivă în stânga <div>-ului, de aceeaşi lăţime ca a acestuia). În sprite-ul nostru, : -100% fixează originea imaginii la marginea stângă a pionului alb şi la poziţionarea imaginii în <div>, pionul de la originea imaginii va fi "poziţionat" în zona fictivă de la stânga <div>-ului, iar piesa care urmează în imagine după pion va fi poziţionată în continuarea acestei zone fictive - deci regele alb va ajunge în <div>-ul nostru.

Analog, : -K*100% fixează originea imaginii pe marginea stângă a pionului alb şi "completează" <div>-ul spre stânga, adăugând fictiv K-1 "div"-uri identice lui; în primul dintre aceste "div"-uri fictive va intra pionul alb, în al doilea "div" va intra regele alb, ş.a.m.d. iar în final, piesa de index K din imagine va apărea în <div>-ul real.

docerpro | Prev | Next