Bufnita — personajul care se uita dupa mouse
Pagina live: /bufnita/. Nu e un port si nu e un fisier cumparat: personajul e desenat direct in SVG, aici, in pagina. Nu se incarca nicio librarie — doar GSAP, care e oricum global pe site.
Ce face
- Bufnita sta pe loc si se uita dupa cursor. Trei piese se misca, in cantitati diferite, si tocmai diferenta dintre ele e ce face gestul sa para o privire si nu o alunecare:
- pupilele merg cel mai mult, in interiorul ochiului
- fata aluneca peste craniu si se comprima — asta e capul care se intoarce
- craniul se misca putin, cat sa aiba adancime
- corpul se apleaca cel mai putin, pivotand la picioare
- Capul se intoarce in ax, nu se inclina. Prima varianta rotea tot capul si arata ca si cum bufnita si-ar culca capul pe umar — alt gest, gresit. Solutia: capul e taiat in doua grupuri. Craniul (cercul negru + smocurile) sta aproape pe loc, iar fata (discul facial, ochii, ciocul) aluneca lateral si isi reduce latimea pana la 91%, exact ce face partea din fata a unei sfere cand se roteste.
- Pupilele sunt limitate la o elipsa, nu la un dreptunghi. Cu limita patrata ar ajunge mai departe pe diagonala decat lateral, iar ochii arata stramb in colturi. Vectorul se normalizeaza la cercul unitate inainte de a fi inmultit cu raza.
- Clipeste la 3–7 secunde, aleator, prin turtirea grupului ochiului. Fara asta pare inghetata.
- Cand nu ai mouse — pe telefon, sau pur si simplu inainte sa-l misti — se uita singura in jur, pe doua sinusoide lente. Aceeasi solutie ca la Albina, fara nicio detectie de dispozitiv.
Cum e desenata
Un singur <svg> cu viewBox="0 0 400 540", scris de mana. Structura conteaza, pentru ca JS-ul gaseste piesele dupa nume:
ow-body <- se apleaca, pivot la picioare
ow-skull <- cercul negru + smocurile
ow-face <- disc facial + ochi + cioc
ow-eye-l / ow-eye-r <- se turtesc la clipit
ow-pupil-l / ow-pupil-r <- iris + pupila + licarire
ow-feet <- INAFARA lui ow-body: bufnita se apleaca, labele stau
Ochiul e construit in trei cercuri concentrice: alb r=50, iris r=34, pupila r=24, plus o licarire alba decentrata. Grupul iris+pupila+licarire e cel care se misca, nu doar pupila — asa cum face un ochi adevarat. Diferenta dintre alb si iris, 16px, e tot bugetul de miscare; constantele folosite sunt 15 pe orizontala si 12 pe verticala, deci irisul nu atinge niciodata marginea.
Culori
Patru culori, toate deja existente pe site — nu s-a inventat nicio paleta:
| Culoare | Unde | De unde vine |
|---|---|---|
#fff318 |
fundalul paginii | paginile cu albina |
#111111 |
craniu, corp, pupile | negrul paginilor |
#ffffff |
disc facial, burta, albul ochiului | idem |
#FECB00 |
iris, cioc, labe | galbenul din albina Lottie |
Reglaje
Patru numere sus in JS schimba caracterul:
| Constanta | Acum | Ce face |
|---|---|---|
FACE_X |
22px | cat de mult se intoarce capul |
FACE_SQUASH_X |
0.10 | cat se comprima fata la intoarcere completa |
PUPIL_RX / PUPIL_RY |
15 / 12 | cat merg pupilele |
EASE |
0.12 | cat de repede se uita dupa tine |
Capcane
- Capcana zilei:
gsap.ticker.add()nu porneste un ticker adormit. La prima publicare pagina era complet moarta si nimic nu semnala de ce: elementele existau,gsap.set()mergea perfect, consola era goala, dargsap.ticker.frameramanea0. GSAP isi adoarme ticker-ul cand nu are nimic de animat, iar aici nu exista niciun tween care sa-l trezeasca — spre deosebire de Albina, unde lottie pornea oricum animatii. Fixul e o linie,gsap.ticker.wake(), la finalul initializarii. Daca o animatie pegsap.tickernu porneste, verifica intaigsap.ticker.frame: daca e 0, asta e. - Interpolarea pe cadru depinde de monitor.
x += (tinta - x) * 0.12merge de doua ori mai repede la 120Hz. Toate cele patru urmariri trec prinapproach(), corectat cu1 - Math.pow(1 - ease, dt * 60). - Ancora nu se citeste din pozitia ochilor. Ochii se misca; o ancora care se misca odata cu ei se auto-alimenteaza si bufnita incepe sa tremure singura. Ancora se calculeaza din chenarul
<svg>-ului plus coordonatele ochilor in unitati viewBox — un punct fix. - Matricea citita din
getComputedStylenu iti da inapoi valorile pe care le-ai scris. Cand folosestisvgOrigin, GSAP coace deplasarea fata de origine in componentele de translatie. Vei citi 34.8 acolo unde ai scris 22. Nu e o eroare — verifica rotatia si scara, care sunt curate, si lasa translatia in pace. - Personajul din referinta initiala era Jerry (Warner Bros./MGM). De aceea bufnita e desenata de la zero: un personaj protejat n-ar fi putut fi refolosit nicaieri.
Pasi de reproducere
- Deseneaza personajul in SVG cu piesele mobile in grupuri numite. Regula: orice se misca separat are nevoie de propriul
id. Labele stau inafara grupului care se apleaca. - Ochiul: trei cercuri concentrice, iar grupul iris+pupila e cel care se misca. Bugetul de deplasare e raza albului minus raza irisului — alege constantele sub aceasta valoare.
- Calculeaza ancora din chenarul svg-ului plus coordonatele ochilor in unitati viewBox. Recalculeaz-o la
resize, niciodata din pozitia curenta a ochilor. - Normalizeaza vectorul spre cursor la cercul unitate, apoi inmulteste-l cu razele. Interpoleaza cu o functie corectata de rata de cadre.
- Capul: nu roti. Aluneca fata peste craniu si redu-i
scaleX. Rotatia e alt gest. - Cheama
gsap.ticker.wake()dupa ce ai inregistrat callback-ul. - Verifica cu masuratori, nu din ochi: pozitia pupilei in cele patru colturi (simetrica si sub limita),
rotationzero pe fata in toate pozitiile,gsap.ticker.frame > 0imediat dupa incarcare.