Opprette en klokke med trigonometrifunksjonene New CSS sin() og cos().

Opprette en klokke med trigonometrifunksjonene New CSS sin() og cos().

Kilde node: 1999799

CSS trigonometri funksjoner er her! Vel, det er de hvis du bruker de nyeste versjonene av Firefox og Safari, altså. Å ha denne typen matematisk kraft i CSS åpner for en hel haug med muligheter. I denne opplæringen tenkte jeg at vi skulle dyppe tærne i vannet for å få en følelse av et par av de nyere funksjonene: sin() og cos().

Det er andre trigonometrifunksjoner i rørledningen - inkludert tan() – så hvorfor fokusere bare på sin() og cos()? De passer tilfeldigvis perfekt for ideen jeg har i tankene, som er å plassere tekst langs kanten av en sirkel. Det har blitt dekket her på CSS-Tricks når Chris delte en tilnærming som bruker en Sass-miks. Det var for seks år siden, så la oss gi den blødende behandling.

Her er hva jeg har i tankene. Igjen, det støttes bare i Firefox og Safari for øyeblikket:

Så det er ikke akkurat som ord som danner en sirkulær form, men vi plasserer teksttegn langs sirkelen for å danne en urskive. Her er noen markeringer vi kan bruke for å sette i gang ting:

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

Deretter er her noen super grunnleggende stiler for .clock-face container. Jeg bestemte meg for å bruke <time> tag med en datetime attributt. 

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

Jeg dekorerte ting litt der inne, men bare for å få den grunnleggende formen og bakgrunnsfargen for å hjelpe oss med å se hva vi gjør. Legg merke til hvordan vi lagrer width verdi i en CSS-variabel. Vi bruker det senere. Ikke mye å se på så langt:

Stor tomatfarget sirkel med en vertikal liste med tallene 1-12 til venstre.

Det ser ut som et slags moderne kunsteksperiment, ikke sant? La oss introdusere en ny variabel, --_r, for å lagre sirkelens radius, som er lik halvparten av sirkelens bredde. På denne måten, hvis bredden (--_w) endringer, radiusverdien (--_r) vil også oppdatere - takket være en annen CSS-matematikkfunksjon, calc():

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

Nå, litt matematikk. En sirkel er 360 grader. Vi har 12 etiketter på klokken vår, så ønsker å plassere tallene hver 30. grader (360 / 12). I matteland begynner en sirkel klokken 3, så middag er faktisk minus 90 grader fra det, som er 270 grader (360 - 90).

La oss legge til en annen variabel, --_d, som vi kan bruke til å sette en grad verdi for hvert tall på urskiven. Vi skal øke verdiene med 30 grader for å fullføre sirkelen vår:

.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, nå er det på tide å skitne hendene våre med sin() og cos() funksjoner! Det vi ønsker å gjøre er å bruke dem til å få X- og Y-koordinatene for hvert tall, slik at vi kan plassere dem riktig døgnet rundt.

Formelen for X-koordinaten er radius + (radius * cos(degree)). La oss koble det til vår nye --_x variabel:

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

Formelen for Y-koordinaten er radius + (radius * sin(degree)). Vi har det vi trenger for å beregne det:

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

Det er noen få ting vi må gjøre for å sette opp tallene, så la oss sette litt grunnleggende styling på dem for å sikre at de er helt plassert og plassert med koordinatene våre:

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

Legge merke til --_sz, som vi skal bruke til width og height av tallene på et øyeblikk. La oss se hva vi har så langt.

Stor tomatfarget sirkel med ikke-sentrerte timenummeretiketter langs kanten.

Dette ser definitivt mer ut som en klokke! Ser du hvordan det øverste venstre hjørnet av hvert tall er plassert på riktig sted rundt sirkelen? Vi må "krympe" radien når vi beregner posisjonene for hvert tall. Vi kan trekke fra størrelsen på et tall (--_sz) fra størrelsen på sirkelen (--_w), før vi beregner radiusen:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Stor tomatfarget sirkel med timenummeretiketter langs den avrundede kanten.

Mye bedre! La oss endre fargene, så det ser mer elegant ut:

En hvit urskive med tall mot en mørkegrå bakgrunn. Klokken har ingen armer.

Vi kunne stoppet her! Vi oppnådde målet om å plassere tekst rundt en sirkel, ikke sant? Men hva er en klokke uten armer for å vise timer, minutter og sekunder?

La oss bruke en enkelt CSS-animasjon for det. Først, la oss legge til tre elementer til i markeringen vår,

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

Så noen felles markering for alle tre armene. Igjen, det meste av dette er bare å sørge for at armene er absolutt plassert og plassert deretter:

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

Vi bruker samme animasjon for alle tre armer:

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

Den eneste forskjellen er tiden de enkelte armene bruker på å gjøre en hel sving. For timer arm, det tar 12 timer å gjøre en hel sving. De animation-duration egenskap godtar kun verdier i millisekunder og sekunder. La oss holde oss til sekunder, som er 43,200 XNUMX sekunder (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Det tar 1 time for minutter arm å gjøre en hel sving. Men vi ønsker at dette skal være en flertrinns animasjon så bevegelsen mellom armene er forskjøvet i stedet for lineær. Vi trenger 60 trinn, ett for hvert minutt:

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

De sekunder arm is nesten det samme som minuttarmen, men varigheten er 60 sekunder i stedet for 60 minutter:

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

La oss oppdatere egenskapene vi opprettet i de vanlige stilene:

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

Hva om vi ønsker å starte på nåværende tidspunkt? Vi trenger litt 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`);

jeg har lagt til id="app" til urskiven og sett to nye egendefinerte egenskaper på den som setter en negativ animation-delay, som Mate Marschalko gjorde da han delte en CSS-klokke. De getHours() metode for JavaScipt's Date objektet bruker 24-timers formatet, så vi bruker remainder operatør for å konvertere den til 12-timers format.

I CSS må vi legge til animation-delay også:

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

Bare en ting til. Bruker CSS @supports og egenskapene vi allerede har opprettet, kan vi tilby en reserve til nettlesere som ikke støtter sin() og cos(). (Takk, 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)))
  }
}

Og voilà! Klokken vår er ferdig! Her er den siste demoen en gang til. Igjen, det støttes bare i Firefox og Safari for øyeblikket.

Hva annet kan vi gjøre?

Bare roter rundt her, men vi kan raskt gjøre klokken om til et sirkulært bildegalleri ved å erstatte <time> tagger med <img> så oppdaterer du bredden (--_w) og radius (--_r) verdier:

La oss prøve en til. Jeg nevnte tidligere hvordan klokken så ut som et moderne kunsteksperiment. Vi kan lene oss inn i det og gjenskape et mønster jeg så på en plakat (som jeg dessverre ikke kjøpte) i et kunstgalleri her om dagen. Så vidt jeg husker, ble den kalt "Måne" og besto av en haug med prikker som dannet en sirkel.

En stor sirkel dannet seg av en haug med mindre fylte sirkler i forskjellige jordfarger.

Vi bruker en uordnet liste denne gangen siden kretsene ikke følger en bestemt rekkefølge. Vi kommer ikke engang til å legge alle listeelementene i markeringen. La oss heller injisere dem med JavaScript og legge til noen få kontroller vi kan bruke for å manipulere det endelige resultatet.

Kontrollene er rekkeviddeinnganger (<input type="range">) som vi pakker inn i en <form> og lytt etter input hendelse.

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

Vi kjører denne metoden på "input", som vil skape en haug med <li> elementer med graden (--_d) variabel vi brukte tidligere brukt på hver enkelt. Vi kan også gjenbruke vår radiusvariabel (--_r).

Jeg vil også at prikkene skal ha forskjellige farger. Så, la oss randomisere (vel, ikke helt randomisert) HSL-fargeverdien for hvert listeelement og lagre det som en ny CSS-variabel, --_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() metoden velger en verdi innenfor et definert tallområde:

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

Og det er det. Vi bruker JavaScript for å gjengi markeringen, men så snart den er gjengitt, trenger vi den egentlig ikke. De sin() og cos() funksjoner hjelper oss med å plassere alle prikkene på de riktige stedene.

Avsluttende tanker

Å plassere ting rundt en sirkel er et ganske grunnleggende eksempel for å demonstrere kreftene til trigonometriske funksjoner sin() og cos(). Men det er virkelig kult at vi får moderne CSS-funksjoner som gir nye løsninger for gamle løsninger. Jeg er sikker på at vi vil se mye mer interessante, komplekse og kreative brukssaker, spesielt ettersom nettleserstøtte kommer til Chrome og Edge.

Tidstempel:

Mer fra CSS triks