Creazione di un orologio con le nuove funzioni trigonometriche CSS sin() e cos()

Creazione di un orologio con le nuove funzioni trigonometriche CSS sin() e cos()

Nodo di origine: 1999799

Le funzioni di trigonometria CSS sono qui! Bene, lo sono se stai usando le ultime versioni di Firefox e Safari, cioè. Avere questo tipo di potere matematico nei CSS apre un sacco di possibilità. In questo tutorial, ho pensato di immergere le dita dei piedi nell'acqua per avere un'idea di un paio delle funzioni più recenti: sin() ed cos().

Ci sono altre funzioni di trigonometria in cantiere, incluso tan() - quindi perché concentrarsi solo su sin() ed cos()? Si dà il caso che siano perfetti per l'idea che ho in mente, ovvero posizionare il testo lungo il bordo di un cerchio. Questo è stato trattato qui su CSS-Tricks quando Chris ha condiviso un approccio che utilizza un mixin Sass. È stato sei anni fa, quindi diamogli il trattamento all'avanguardia.

Ecco cosa ho in mente. Di nuovo, al momento è supportato solo in Firefox e Safari:

Quindi, non è esattamente come le parole che formano una forma circolare, ma stiamo posizionando caratteri di testo lungo il cerchio per formare il quadrante di un orologio. Ecco alcuni markup che possiamo usare per dare il via alle cose:

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

Successivamente, ecco alcuni stili super di base per il .clock-face contenitore. Ho deciso di utilizzare il <time> tag con un datetime attributo. 

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

Ho decorato un po' le cose lì dentro, ma solo per ottenere la forma di base e il colore di sfondo per aiutarci a vedere cosa stiamo facendo. Nota come salviamo il file width valore in a Variabile CSS. Lo useremo più tardi. Non c'è molto da guardare finora:

Grande cerchio color pomodoro con un elenco verticale di numeri 1-12 a sinistra.

Sembra una specie di esperimento di arte moderna, giusto? Introduciamo una nuova variabile, --_r, per memorizzare i cerchi raggio, che è uguale alla metà della larghezza del cerchio. In questo modo, se la larghezza (--_w) cambia, il valore del raggio (--_r) si aggiornerà anche — grazie a un'altra funzione matematica CSS, calc():

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

Ora, un po' di matematica. Un cerchio è di 360 gradi. Abbiamo 12 etichette sul nostro orologio, quindi vogliamo posizionare i numeri ogni 30 gradi (360 / 12). In matematica, un cerchio inizia alle 3, quindi in realtà è mezzogiorno meno 90 gradi da quello, che è di 270 gradi (360 - 90).

Aggiungiamo un'altra variabile, --_d, che possiamo usare per impostare a grado valore per ogni numero sul quadrante dell'orologio. Incrementeremo i valori di 30 gradi per completare il nostro cerchio:

.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, ora è il momento di sporcarci le mani con il sin() ed cos() funzioni! Quello che vogliamo fare è usarli per ottenere le coordinate X e Y per ogni numero in modo da poterli posizionare correttamente intorno al quadrante dell'orologio.

La formula per la coordinata X è radius + (radius * cos(degree)). Inseriamolo nel nostro nuovo --_x variabili:

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

La formula per la coordinata Y è radius + (radius * sin(degree)). Abbiamo quello che ci serve per calcolare che:

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

Ci sono alcune cose di pulizia che dobbiamo fare per impostare i numeri, quindi mettiamo uno stile di base su di essi per assicurarci che siano assolutamente posizionati e posizionati con le nostre coordinate:

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

Preavviso --_sz, che useremo per il width ed height dei numeri in un attimo. Vediamo cosa abbiamo finora.

Grande cerchio color pomodoro con etichette dei numeri delle ore decentrate lungo il bordo.

Questo sembra decisamente più un orologio! Vedi come l'angolo in alto a sinistra di ogni numero è posizionato nella posizione corretta attorno al cerchio? Abbiamo bisogno di "ridurre" il raggio quando calcoliamo le posizioni per ogni numero. Noi possiamo dedurre la dimensione di un numero (--_sz) dalla dimensione del cerchio (--_w), prima di calcolare il raggio:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Grande cerchio color pomodoro con etichette dei numeri delle ore lungo il bordo arrotondato.

Molto meglio! Cambiamo i colori, quindi sembra più elegante:

Un quadrante bianco con numeri su uno sfondo grigio scuro. L'orologio non ha le braccia.

Potremmo fermarci qui! Abbiamo raggiunto l'obiettivo di posizionare il testo attorno a un cerchio, giusto? Ma cos'è un orologio senza braccia per mostrare ore, minuti e secondi?

Usiamo una singola animazione CSS per questo. Innanzitutto, aggiungiamo altri tre elementi al nostro markup,

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

Quindi un markup comune per tutte e tre le braccia. Ancora una volta, la maggior parte di questo è solo assicurarsi che le braccia siano assolutamente posizionate e posizionate di conseguenza:

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

Useremo il stessa animazione per tutte e tre le braccia:

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

L'unica differenza è il tempo impiegato dalle singole braccia per compiere un giro completo. Per il braccio delle ore, prende 12 ore per fare un giro completo. IL animation-duration La proprietà accetta solo valori in millisecondi e secondi. Rimaniamo sui secondi, ovvero 43,200 secondi (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Prende 1 ora per l' braccio dei minuti fare un giro completo. Ma vogliamo che questo sia un animazione in più fasi quindi il movimento tra le braccia è sfalsato piuttosto che lineare. Avremo bisogno di 60 passaggi, uno per ogni minuto:

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

I braccio dei secondi is quasi la stessa come il braccio dei minuti, ma la durata è di 60 secondi invece di 60 minuti:

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

Aggiorniamo le proprietà che abbiamo creato negli stili comuni:

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

E se volessimo iniziare all'ora corrente? Abbiamo bisogno di un po' di 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`);

ho aggiunto id="app" al quadrante dell'orologio e impostare due nuove proprietà personalizzate su di esso che impostano un valore negativo animation-delay, come ha fatto Mate Marschalko quando ha condiviso un orologio solo CSSgetHours() metodo di JavaScipt Date object usa il formato 24 ore, quindi usiamo il formato remainder operatore per convertirlo nel formato 12 ore.

Nel CSS, dobbiamo aggiungere il file animation-delay anche:

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

Soltanto un'altra cosa. Utilizzo dei CSS @supports e le proprietà che abbiamo già creato, possiamo fornire un fallback ai browser che non supportano sin() ed cos(). (Grazie, 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)))
  }
}

E, voilà! Il nostro orologio è finito! Ecco la demo finale ancora una volta. Ancora una volta, al momento è supportato solo in Firefox e Safari.

Cos'altro possiamo fare?

Sto solo scherzando qui, ma possiamo trasformare rapidamente il nostro orologio in una galleria di immagini circolare sostituendo il <time> tag con <img> quindi aggiornando la larghezza (--_w) e raggio (--_r) i valori:

Proviamo un altro. Ho accennato in precedenza a come l'orologio assomigliasse a un esperimento di arte moderna. Possiamo appoggiarci a questo e ricreare uno schema che ho visto su un poster (che purtroppo non ho comprato) in una galleria d'arte l'altro giorno. Per quanto ricordo, si chiamava "Luna" e consisteva in un mucchio di punti che formavano un cerchio.

Un grande cerchio formato da un gruppo di cerchi pieni più piccoli di vari colori della terra.

Questa volta useremo un elenco non ordinato poiché i cerchi non seguono un ordine particolare. Non inseriremo nemmeno tutti gli elementi dell'elenco nel markup. Invece, iniettiamoli con JavaScript e aggiungiamo alcuni controlli che possiamo usare per manipolare il risultato finale.

I controlli sono ingressi di intervallo (<input type="range">) che avvolgeremo in a <form> e ascolta il input evento.

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

Eseguiremo questo metodo su "input", che creerà un mucchio di <li> elementi con il grado (--_d) variabile che abbiamo usato in precedenza applicato a ciascuno di essi. Possiamo anche riutilizzare la nostra variabile di raggio (--_r).

Voglio anche che i punti siano di colori diversi. Quindi, randomizziamo (beh, no completamente randomizzato) il valore del colore HSL per ogni elemento dell'elenco e memorizzarlo come una nuova variabile 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;
}

I random() metodo seleziona un valore all'interno di un intervallo definito di numeri:

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

E questo è tutto. Usiamo JavaScript per eseguire il rendering del markup, ma non appena viene visualizzato, non ne abbiamo davvero bisogno. IL sin() ed cos() le funzioni ci aiutano a posizionare tutti i punti nei punti giusti.

Conclusioni

Posizionare le cose attorno a un cerchio è un esempio piuttosto semplice per dimostrare i poteri delle funzioni trigonometriche come sin() ed cos(). Ma è veramente bello che stiamo ottenendo moderne funzionalità CSS che forniscono nuove soluzioni per vecchie soluzioni alternative, sono sicuro che vedremo casi d'uso molto più interessanti, complessi e creativi, soprattutto quando il supporto del browser arriverà su Chrome e Edge.

Timestamp:

Di più da Trucchi CSS