Een klok maken met de nieuwe CSS-functies sin() en cos() trigonometrie

Een klok maken met de nieuwe CSS-functies sin() en cos() trigonometrie

Bronknooppunt: 1999799

CSS trigonometriefuncties zijn er! Welnu, dat is het geval als u de nieuwste versies van Firefox en Safari gebruikt. Het hebben van dit soort wiskundige kracht in CSS opent een hele reeks mogelijkheden. In deze tutorial dacht ik dat we onze tenen in het water zouden dopen om een โ€‹โ€‹idee te krijgen van een paar van de nieuwere functies: sin() en cos().

Er zitten nog andere trigonometrische functies in de pijplijn, waaronder tan() - dus waarom alleen focussen op sin() en cos()? Ze passen perfect bij het idee dat ik in gedachten heb, namelijk tekst langs de rand van een cirkel plaatsen. Dat is hier behandeld op CSS-Tricks wanneer Chris deelde een benadering die een Sass-mix gebruikt. Dat was zes jaar geleden, dus laten we het een bloedstollende behandeling geven.

Dit is wat ik in gedachten heb. Nogmaals, het wordt momenteel alleen ondersteund in Firefox en Safari:

Het is dus niet precies zoals woorden die een cirkelvorm vormen, maar we plaatsen teksttekens langs de cirkel om een โ€‹โ€‹wijzerplaat te vormen. Hier is wat opmaak die we kunnen gebruiken om dingen op gang te brengen:

<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>

Vervolgens zijn hier enkele super basic stijlen voor de .clock-face container. Ik besloot om de <time> tag met a datetime attribuut. 

.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);
}

Ik heb de dingen daar een beetje versierd, maar alleen om de basisvorm en achtergrondkleur te krijgen om ons te helpen zien wat we aan het doen zijn. Let op hoe we de width waarde in a CSS-variabele. Die gebruiken we later. Tot nu toe niet veel om naar te kijken:

Grote tomaatkleurige cirkel met links een verticale lijst met nummers 1-12.

Het lijkt op een soort modern kunstexperiment, toch? Laten we een nieuwe variabele introduceren, --_r, om de cirkels op te slaan radius, wat gelijk is aan de helft van de breedte van de cirkel. Op deze manier, als de breedte (--_w) verandert, verandert de straalwaarde (--_r) wordt ook bijgewerkt โ€” dankzij een andere CSS-wiskundige functie, calc():

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

Nu een beetje wiskunde. Een cirkel is 360 graden. We hebben 12 labels op onze klok, dus we willen de cijfers elke 30 graden plaatsen (360 / 12). In wiskundeland begint een cirkel om 3 uur, dus eigenlijk is het middag min 90 graden daarvan, dat is 270 graden (360 - 90).

Laten we nog een variabele toevoegen, --_d, die we kunnen gebruiken om a in te stellen mate waarde voor elk nummer op de wijzerplaat. We gaan de waarden met 30 graden verhogen om onze cirkel te voltooien:

.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, nu is het tijd om onze handen vuil te maken met de sin() en cos() functies! Wat we willen doen, is ze gebruiken om de X- en Y-coรถrdinaten voor elk getal te krijgen, zodat we ze op de juiste manier rond de wijzerplaat kunnen plaatsen.

De formule voor de X-coรถrdinaat is radius + (radius * cos(degree)). Laten we dat aansluiten op onze nieuwe --_x variabele:

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

De formule voor de Y-coรถrdinaat is radius + (radius * sin(degree)). We hebben wat we nodig hebben om dat te berekenen:

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

Er zijn een paar huishoudelijke dingen die we moeten doen om de nummers in te stellen, dus laten we ze wat basisstijlen geven om ervoor te zorgen dat ze absoluut gepositioneerd en geplaatst zijn met onze coรถrdinaten:

.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);
}

Opmerken --_sz, die we zullen gebruiken voor de width en height van de nummers in een oogwenk. Laten we eens kijken wat we tot nu toe hebben.

Grote tomaatkleurige cirkel met niet-gecentreerde uurnummerlabels langs de rand.

Dit lijkt zeker meer op een klok! Zie je hoe de linkerbovenhoek van elk nummer op de juiste plaats rond de cirkel staat? We moeten de straal "verkleinen" bij het berekenen van de posities voor elk nummer. We kunnen aftrekken de grootte van een getal (--_sz) van de grootte van de cirkel (--_w), voordat we de straal berekenen:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Grote tomaatkleurige cirkel met uurnummerlabels langs de afgeronde rand.

Veel beter! Laten we de kleuren veranderen, zodat het er eleganter uitziet:

Een witte wijzerplaat met cijfers tegen een donkergrijze achtergrond. De klok heeft geen armen.

We zouden hier kunnen stoppen! We hebben het doel bereikt om tekst rond een cirkel te plaatsen, toch? Maar wat is een klok zonder armen om uren, minuten en seconden aan te geven?

Laten we daarvoor een enkele CSS-animatie gebruiken. Laten we eerst nog drie elementen aan onze opmaak toevoegen,

<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>

Dan een gemeenschappelijke opmaak voor alle drie de armen. Nogmaals, het meeste hiervan is om ervoor te zorgen dat de armen absoluut gepositioneerd en dienovereenkomstig geplaatst zijn:

.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);
}

We gebruiken de dezelfde animatie voor alle drie de armen:

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

Het enige verschil is de tijd die de individuele armen nodig hebben om een โ€‹โ€‹volledige draai te maken. Voor de uur arm, het duurt 12 uur kunt opladen om een โ€‹โ€‹volledige draai te maken. De animation-duration eigenschap accepteert alleen waarden in milliseconden en seconden. Laten we het bij seconden houden, dat is 43,200 seconden (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Het duurt 1 uur voor de minuten arm om een โ€‹โ€‹volledige draai te maken. Maar we willen dat dit een animatie in meerdere stappen dus de beweging tussen de armen is verspringend in plaats van lineair. We hebben 60 stappen nodig, รฉรฉn voor elke minuut:

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

De seconden arm is bijna hetzelfde als de minutenarm, maar de duur is 60 seconden in plaats van 60 minuten:

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

Laten we de eigenschappen bijwerken die we in de algemene stijlen hebben gemaakt:

.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;
}

Wat als we op het huidige tijdstip willen beginnen? We hebben een klein beetje JavaScript nodig:

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`);

Ik heb toegevoegd id="app" naar de wijzerplaat en stel er twee nieuwe aangepaste eigenschappen op in die een negatief instellen animation-delay, zoals Mate Marschalko deed toen hij een klok met alleen CSS deelde. De getHours() methode van JavaScipt Date object gebruikt het 24-uurs formaat, dus gebruiken we de remainder operator om het om te zetten in 12-uurs formaat.

In de CSS moeten we de animation-delay ook:

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

Nog een ding. CSS gebruiken @supports en de eigenschappen die we al hebben gemaakt, kunnen we een terugval bieden naar browsers die dit niet ondersteunen sin() en cos(). (Bedankt, 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)))
  }
}

En voila! Onze klok is klaar! Hier is de laatste demo nog een keer. Nogmaals, het wordt momenteel alleen ondersteund in Firefox en Safari.

Wat anders kunnen we doen?

Gewoon wat rommelen hier, maar we kunnen onze klok snel veranderen in een cirkelvormige afbeeldingengalerij door de <time> tags met <img> vervolgens de breedte bijwerken (--_w) en straal (--_r) waarden:

Laten we er nog een proberen. Ik noemde eerder hoe de klok eruitzag als een modern kunstexperiment. We kunnen daarop ingaan en een patroon opnieuw creรซren dat ik onlangs op een poster zag (die ik helaas niet heb gekocht) in een kunstgalerie. Voor zover ik me herinner, heette het "Maan" en bestond het uit een aantal stippen die een cirkel vormden.

Een grote cirkel gevormd uit een aantal kleinere gevulde cirkels van verschillende aardetinten.

We gebruiken deze keer een ongeordende lijst omdat de cirkels geen bepaalde volgorde volgen. We gaan niet eens alle lijstitems in de opmaak plaatsen. Laten we ze in plaats daarvan injecteren met JavaScript en een paar bedieningselementen toevoegen die we kunnen gebruiken om het eindresultaat te manipuleren.

De bedieningselementen zijn bereikingangen (<input type="range">) die we inpakken in een <form> en luister naar de input evenement.

<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>

We zullen deze methode uitvoeren op "input", wat een heleboel zal creรซren <li> elementen met de graad (--_d) variabele die we eerder gebruikten toegepast op elke variabele. We kunnen ook onze radiusvariabele hergebruiken (--_r).

Ik wil ook dat de stippen verschillende kleuren hebben. Dus laten we randomiseren (nou ja, niet compleet gerandomiseerd) de HSL-kleurwaarde voor elk lijstitem en sla deze op als een nieuwe CSS-variabele, --_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;
}

De random() methode kiest een waarde binnen een gedefinieerd bereik van getallen:

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

En dat is het. We gebruiken JavaScript om de markup weer te geven, maar zodra het wordt weergegeven, hebben we het niet echt nodig. De sin() en cos() functies helpen ons alle stippen op de juiste plek te plaatsen.

Laatste gedachten

Dingen rond een cirkel plaatsen is een vrij eenvoudig voorbeeld om de krachten van trigonometrische functies zoals te demonstreren sin() en cos(). Maar het is werkelijk cool dat we moderne CSS-functies krijgen die nieuwe oplossingen bieden voor oude tijdelijke oplossingen. Ik weet zeker dat we veel interessantere, complexere en creatievere use-cases zullen zien, vooral omdat browserondersteuning naar Chrome en Edge komt.

Tijdstempel:

Meer van CSS-trucs