Crearea unui ceas cu noile funcții trigonometrice CSS sin() și cos().

Crearea unui ceas cu noile funcții trigonometrice CSS sin() și cos().

Nodul sursă: 1999799

Funcțiile de trigonometrie CSS sunt aici! Ei bine, acestea sunt dacă utilizați cele mai recente versiuni de Firefox și Safari, adică. Având acest tip de putere matematică în CSS deschide o mulțime de posibilități. În acest tutorial, m-am gândit să ne scufundăm degetele de la picioare în apă pentru a avea o senzație pentru câteva dintre funcțiile mai noi: sin() și cos().

Există și alte funcții trigonometrice în curs - inclusiv tan() — deci de ce să te concentrezi doar pe sin() și cos()? Se întâmplă să fie perfecte pentru ideea pe care o am în minte, care este să plasez textul de-a lungul marginii unui cerc. Acest lucru a fost acoperit aici pe CSS-Tricks când Chris a împărtășit o abordare care folosește un mixin Sass. Asta a fost acum șase ani, așa că haideți să-i dăm un tratament de vârf.

Iată ce am în minte. Din nou, momentan este acceptat doar în Firefox și Safari:

Deci, nu este exact ca cuvintele care formează o formă circulară, dar plasăm caractere de text de-a lungul cercului pentru a forma un cadran de ceas. Iată câteva markupuri pe care le putem folosi pentru a începe lucrurile:

<div class="clock"> <div class="clock-face"> <time datetime="12:00">12</time> <time datetime="1:00">1</time> <time datetime="2:00">2</time> <time datetime="3:00">3</time> <time datetime="4:00">4</time> <time datetime="5:00">5</time> <time datetime="6:00">6</time> <time datetime="7:00">7</time> <time datetime="8:00">8</time> <time datetime="9:00">9</time> <time datetime="10:00">10</time> <time datetime="11:00">11</time> </div>
</div>

În continuare, iată câteva stiluri super de bază pentru .clock-face recipient. Am decis să folosesc <time> eticheta cu a datetime atribut. 

.clock { --_ow: clamp(5rem, 60vw, 40rem); --_w: 88cqi; aspect-ratio: 1; background-color: tomato; border-radius: 50%; container-type: inline; display: grid; height: var(--_ow); place-content: center; position: relative; width var(--_ow);
}

Am decorat puțin lucrurile acolo, dar doar pentru a obține forma de bază și culoarea de fundal, care să ne ajute să vedem ce facem. Observați cum salvăm width valoare în a variabilă CSS. O vom folosi mai târziu. Nu prea multe de văzut până acum:

Cerc mare de culoare roșii cu o listă verticală de numere 1-12 în stânga.

Arată ca un fel de experiment de artă modernă, nu? Să introducem o nouă variabilă, --_r, pentru a stoca cercul rază, care este egal cu jumătate din lățimea cercului. În acest fel, dacă lățimea (--_w) se modifică, valoarea razei (--_r) se va actualiza, de asemenea, datorită unei alte funcții matematice CSS, calc():

.clock { --_w: 300px; --_r: calc(var(--_w) / 2); /* rest of styles */
}

Acum, puțină matematică. Un cerc are 360 ​​de grade. Avem 12 etichete pe ceas, așa că doriți să plasați numerele la fiecare 30 de grade (360 / 12). În matematică, un cerc începe la ora 3, deci amiază este de fapt minus 90 grade de la asta, care este 270 de grade (360 - 90).

Să adăugăm o altă variabilă, --_d, pe care îl putem folosi pentru a seta a grad valoare pentru fiecare număr de pe cadranul ceasului. Vom crește valorile cu 30 de grade pentru a ne completa cercul:

.clock time:nth-child(1) { --_d: 270deg; }
.clock time:nth-child(2) { --_d: 300deg; }
.clock time:nth-child(3) { --_d: 330deg; }
.clock time:nth-child(4) { --_d: 0deg; }
.clock time:nth-child(5) { --_d: 30deg; }
.clock time:nth-child(6) { --_d: 60deg; }
.clock time:nth-child(7) { --_d: 90deg; }
.clock time:nth-child(8) { --_d: 120deg; }
.clock time:nth-child(9) { --_d: 150deg; }
.clock time:nth-child(10) { --_d: 180deg; }
.clock time:nth-child(11) { --_d: 210deg; }
.clock time:nth-child(12) { --_d: 240deg; }

OK, acum este momentul să ne murdărim mâinile cu sin() și cos() functii! Ceea ce vrem să facem este să le folosim pentru a obține coordonatele X și Y pentru fiecare număr, astfel încât să le putem plasa corect pe fața ceasului.

Formula pentru coordonatele X este radius + (radius * cos(degree)). Să conectăm asta la noul nostru --_x variabilă:

--_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));

Formula pentru coordonata Y este radius + (radius * sin(degree)). Avem ceea ce avem nevoie pentru a calcula că:

--_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));

Există câteva lucruri de menaj pe care trebuie să le facem pentru a configura numerele, așa că haideți să le punem un stil de bază pentru a ne asigura că sunt poziționate și plasate cu coordonatele noastre:

.clock-face time { --_x: calc(var(--_r) + (var(--_r) * cos(var(--_d)))); --_y: calc(var(--_r) + (var(--_r) * sin(var(--_d)))); --_sz: 12cqi; display: grid; height: var(--_sz); left: var(--_x); place-content: center; position: absolute; top: var(--_y); width: var(--_sz);
}

Observa --_sz, pe care îl vom folosi pentru width și height a numerelor într-o clipă. Să vedem ce avem până acum.

Cerc mare de culoare roșii, cu etichete cu numere de oră decentrate de-a lungul marginii.

Acesta cu siguranță arată mai mult ca un ceas! Vedeți cum este poziționat colțul din stânga sus al fiecărui număr în locul corect în jurul cercului? Trebuie să „micșorăm” raza atunci când calculăm pozițiile pentru fiecare număr. Putem deduce dimensiunea unui număr (--_sz) de la dimensiunea cercului (--_w), înainte de a calcula raza:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Cerc mare de culoare roșii, cu etichete cu numărul de ore de-a lungul marginii rotunjite.

Mult mai bine! Să schimbăm culorile, astfel încât să arate mai elegant:

Un cadran alb de ceas cu numere pe un fundal gri închis. Ceasul nu are brațe.

Ne-am putea opri chiar aici! Am îndeplinit obiectivul de a plasa textul în jurul unui cerc, nu? Dar ce este un ceas fără brațe pentru a afișa ore, minute și secunde?

Să folosim o singură animație CSS pentru asta. Mai întâi, să adăugăm încă trei elemente la marcajul nostru,

<div class="clock"> <!-- after <time>-tags --> <span class="arm seconds"></span> <span class="arm minutes"></span> <span class="arm hours"></span> <span class="arm center"></span>
</div>

Apoi un marcaj comun pentru toate cele trei brațe. Din nou, cea mai mare parte este doar să vă asigurați că brațele sunt poziționate și plasate în mod corespunzător:

.arm { background-color: var(--_abg); border-radius: calc(var(--_aw) * 2); display: block; height: var(--_ah); left: calc((var(--_w) - var(--_aw)) / 2); position: absolute; top: calc((var(--_w) / 2) - var(--_ah)); transform: rotate(0deg); transform-origin: bottom; width: var(--_aw);
}

Vom folosi aceeași animație pentru toate cele trei brate:

@keyframes turn { to { transform: rotate(1turn); }
}

Singura diferență este timpul necesar brațelor individuale pentru a face o întoarcere completă. Pentru ore braț, este nevoie de 12 oră pentru a face o întoarcere completă. The animation-duration proprietatea acceptă numai valori în milisecunde și secunde. Să rămânem cu secundele, adică 43,200 de secunde (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Este nevoie de 1 oră pentru minute braț a face o tură completă. Dar vrem ca acesta să fie un animație în mai mulți pași deci mișcarea dintre brațe este mai degrabă eșalonată decât liniară. Vom avea nevoie de 60 de pași, câte unul pentru fiecare minut:

animation: turn 3600s steps(60, end) infinite;

brațul de secunde is aproape la fel ca brațul minutelor, dar durata este de 60 de secunde în loc de 60 de minute:

animation: turn 60s steps(60, end) infinite;

Să actualizăm proprietățile pe care le-am creat în stilurile comune:

.seconds { --_abg: hsl(0, 5%, 40%); --_ah: 145px; --_aw: 2px; animation: turn 60s steps(60, end) infinite;
}
.minutes { --_abg: #333; --_ah: 145px; --_aw: 6px; animation: turn 3600s steps(60, end) infinite;
}
.hours { --_abg: #333; --_ah: 110px; --_aw: 6px; animation: turn 43200s linear infinite;
}

Dacă vrem să începem la ora curentă? Avem nevoie de puțin JavaScript:

const time = new Date();
const hour = -3600 * (time.getHours() % 12);
const mins = -60 * time.getMinutes();
app.style.setProperty('--_dm', `${mins}s`);
app.style.setProperty('--_dh', `${(hour+mins)}s`);

am adăugat id="app" la cadranul ceasului și setați două noi proprietăți personalizate pe acesta care setează un negativ animation-delay, așa cum a făcut Mate Marschalko când a partajat un ceas numai CSS.  getHours() metoda lui JavaScipt Date obiectul folosește formatul de 24 de ore, așa că folosim remainder operator pentru a-l converti în format de 12 ore.

În CSS, trebuie să adăugăm animation-delay de asemenea:

.minutes { animation-delay: var(--_dm, 0s); /* other styles */
} .hours { animation-delay: var(--_dh, 0s); /* other styles */
}

Încă un lucru. Folosind CSS @supports și proprietățile pe care le-am creat deja, putem oferi o rezervă pentru browserele care nu suportă sin() și cos(). (Mulțumesc, Temani Afif!):

@supports not (left: calc(1px * cos(45deg))) {
  time {
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%,-50%) rotate(var(--_d)) translate(var(--_r)) rotate(calc(-1*var(--_d)))
  }
}

Și voilà! Ceasul nostru este gata! Iată încă o dată demonstrația finală. Din nou, momentan este acceptat doar în Firefox și Safari.

Ce mai putem face?

Pur și simplu încurcăm aici, dar ne putem transforma rapid ceasul într-o galerie de imagini circulară înlocuind <time> etichete cu <img> apoi actualizați lățimea (--_w) și raza (--_r) valori:

Să mai încercăm unul. Am menționat mai devreme cum arăta ceasul ca un experiment de artă modernă. Ne putem sprijini în asta și recreăm un model pe care l-am văzut pe un poster (pe care, din păcate, nu l-am cumpărat) într-o galerie de artă zilele trecute. După cum îmi amintesc, se numea „Luna” și consta dintr-o grămadă de puncte care formează un cerc.

Un cerc mare s-a format dintr-o grămadă de cercuri mai mici umplute de diferite culori de pământ.

Vom folosi de data aceasta o listă neordonată, deoarece cercurile nu urmează o anumită ordine. Nici măcar nu vom pune toate elementele din listă în marcaj. În schimb, să le injectăm JavaScript și să adăugăm câteva controale pe care le putem folosi pentru a manipula rezultatul final.

Comenzile sunt intrări de gamă (<input type="range">) pe care o vom înveli într-o <form> și ascultă pentru input eveniment.

<form id="controls"> <fieldset> <label>Number of rings <input type="range" min="2" max="12" value="10" id="rings" /> </label> <label>Dots per ring <input type="range" min="5" max="12" value="7" id="dots" /> </label> <label>Spread <input type="range" min="10" max="40" value="40" id="spread" /> </label> </fieldset>
</form>

Vom rula această metodă pe „input”, care va crea o grămadă de <li> elemente cu gradul (--_d) variabilă pe care am folosit-o mai devreme aplicată fiecăruia. De asemenea, putem reutiliza variabila noastră de rază (--_r).

De asemenea, vreau ca punctele să aibă culori diferite. Deci, hai să facem randomizare (ei bine, nu complet randomizat) valoarea culorii HSL pentru fiecare element din listă și stocați-o ca o nouă variabilă CSS, --_bgc:

const update = () => { let s = ""; for (let i = 1; i <= rings.valueAsNumber; i++) { const r = spread.valueAsNumber * i; const theta = coords(dots.valueAsNumber * i); for (let j = 0; j < theta.length; j++) { s += `<li style="--_d:${theta[j]};--_r:${r}px;--_bgc:hsl(${random( 50, 25 )},${random(90, 50)}%,${random(90, 60)}%)"></li>`; } } app.innerHTML = s;
}

random() metoda alege o valoare dintr-un interval definit de numere:

const random = (max, min = 0, f = true) => f ? Math.floor(Math.random() * (max - min) + min) : Math.random() * max;

Si asta e. Folosim JavaScript pentru a reda marcajul, dar de îndată ce este redat, nu prea avem nevoie de el. The sin() și cos() funcțiile ne ajută să poziționăm toate punctele în locurile potrivite.

Gânduri finale

Plasarea lucrurilor în jurul unui cerc este un exemplu destul de simplu pentru a demonstra puterile funcțiilor trigonometrice, cum ar fi sin() și cos(). Dar e într-adevăr mișto că primim funcții CSS moderne care oferă soluții noi pentru soluții vechi. Sunt sigur că vom vedea cazuri de utilizare mult mai interesante, complexe și creative, mai ales că suportul browserului vine la Chrome și Edge.

Timestamp-ul:

Mai mult de la CSS Trucuri