Oprettelse af et ur med den nye CSS sin() og cos() trigonometrifunktioner

Oprettelse af et ur med den nye CSS sin() og cos() trigonometrifunktioner

Kildeknude: 1999799

CSS trigonometri funktioner er her! Nå, det er de, hvis du bruger de nyeste versioner af Firefox og Safari, altså. At have denne form for matematisk kraft i CSS åbner en hel masse muligheder. I denne tutorial tænkte jeg, at vi ville dyppe tæerne i vandet for at få en fornemmelse af et par af de nyere funktioner: sin() , cos().

Der er andre trigonometrifunktioner i pipelinen - herunder tan() - så hvorfor kun fokusere på sin() , cos()? De er tilfældigvis perfekte til den idé, jeg har i tankerne, som er at placere tekst langs kanten af ​​en cirkel. Det er blevet dækket her på CSS-Tricks hvornår Chris delte en tilgang, der bruger en Sass-mixin. Det var seks år siden, så lad os give den blødende kant-behandling.

Her er hvad jeg har i tankerne. Igen, det er kun understøttet i Firefox og Safari i øjeblikket:

Så det er ikke præcis som ord, der danner en cirkulær form, men vi placerer teksttegn langs cirklen for at danne en urskive. Her er nogle markeringer, vi kan bruge til at sætte gang i tingene:

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

Dernæst er her nogle super basic styles til .clock-face beholder. Jeg besluttede at bruge <time> tag med a datetime attribut. 

.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 dekorerede tingene lidt derinde, men kun for at få den grundlæggende form og baggrundsfarve til at hjælpe os med at se, hvad vi laver. Læg mærke til, hvordan vi gemmer width værdi i en CSS variabel. Det bruger vi senere. Ikke meget at se på indtil videre:

Stor tomatfarvet cirkel med en lodret liste med tallene 1-12 til venstre.

Det ligner en slags moderne kunsteksperiment, ikke? Lad os introducere en ny variabel, --_r, for at gemme cirklens radius, hvilket er lig med halvdelen af ​​cirklens bredde. På denne måde, hvis bredden (--_w) ændres, radiusværdien (--_r) vil også opdatere - takket være en anden CSS-matematikfunktion, calc():

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

Nu lidt matematik. En cirkel er 360 grader. Vi har 12 etiketter på vores ur, så ønsker at placere tallene hver 30 grader (360 / 12). I matematikland begynder en cirkel ved 3-tiden, så middag er faktisk minus 90 grader fra det, som er 270 grader (360 - 90).

Lad os tilføje en anden variabel, --_d, som vi kan bruge til at sætte en grad værdi for hvert tal på urskiven. Vi vil øge værdierne med 30 grader for at fuldende vores cirkel:

.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 er det tid til at få hænderne snavsede med sin() , cos() funktioner! Det, vi vil gøre, er at bruge dem til at få X- og Y-koordinaterne for hvert tal, så vi kan placere dem korrekt døgnet rundt.

Formlen for X-koordinaten er radius + (radius * cos(degree)). Lad os sætte det i vores nye --_x variabel:

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

Formlen for Y-koordinaten er radius + (radius * sin(degree)). Vi har hvad vi skal bruge for at beregne det:

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

Der er et par huslige ting, vi skal gøre for at sætte tallene op, så lad os sætte nogle grundlæggende stil på dem for at sikre, at de er helt placeret og placeret med vores koordinater:

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

Varsel --_sz, som vi vil bruge til width , height af tallene på et øjeblik. Lad os se, hvad vi har indtil videre.

Stor tomatfarvet cirkel med ikke-centrerede timenummeretiketter langs kanten.

Dette ligner bestemt mere et ur! Se, hvordan det øverste venstre hjørne af hvert tal er placeret på det rigtige sted rundt om cirklen? Vi skal "krympe" radius, når vi beregner positionerne for hvert tal. Vi kan fradrage størrelsen af ​​et tal (--_sz) fra størrelsen af ​​cirklen (--_w), før vi beregner radius:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Stor tomatfarvet cirkel med timetalsetiketter langs dens afrundede kant.

Meget bedre! Lad os ændre farverne, så det ser mere elegant ud:

En hvid urskive med tal mod en mørkegrå baggrund. Uret har ingen arme.

Vi kunne stoppe lige her! Vi nåede målet om at placere tekst omkring en cirkel, ikke? Men hvad er et ur uden arme til at vise timer, minutter og sekunder?

Lad os bruge en enkelt CSS-animation til det. Lad os først tilføje tre elementer mere til vores opmærkning,

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

Derefter nogle fælles markeringer for alle tre arme. Igen, det meste af dette er bare at sørge for, at armene er absolut placeret og placeret i overensstemmelse hermed:

.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 bruger samme animation for alle tre arme:

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

Den eneste forskel er den tid det tager de enkelte arme at lave en hel omgang. For timer arm, det tager 12 timer at lave en hel omgang. Det animation-duration egenskaben accepterer kun værdier i millisekunder og sekunder. Lad os holde os til sekunder, som er 43,200 sekunder (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Det tager 1 time for minutter arm at lave en hel omgang. Men vi ønsker, at dette skal være en animation i flere trin så bevægelsen mellem armene er forskudt i stedet for lineær. Vi skal bruge 60 trin, et for hvert minut:

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

sekunder arm is næsten det samme som minutarmen, men varigheden er 60 sekunder i stedet for 60 minutter:

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

Lad os opdatere de egenskaber, vi har oprettet i de almindelige stilarter:

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

Hvad hvis vi vil starte på det nuværende tidspunkt? Vi har brug for lidt 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 tilføjet id="app" til urskiven og sæt to nye brugerdefinerede egenskaber på den, der sætter et negativ animation-delay, som Mate Marschalko gjorde da han delte et ur, der kun var CSS. Det getHours() metode til JavaScipt's Date objektet bruger 24-timers formatet, så vi bruger remainder operatør at konvertere det til 12-timers format.

I CSS skal vi tilføje animation-delay såvel:

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

Bare en ting mere. Brug af CSS @supports og de egenskaber, vi allerede har oprettet, kan vi give et alternativ til browsere, der ikke understøtter sin() , cos(). (Tak, 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à! Vores ur er færdigt! Her er den sidste demo endnu en gang. Igen, det er kun understøttet i Firefox og Safari i øjeblikket.

Hvad kan vi ellers gøre?

Bare roder rundt her, men vi kan hurtigt forvandle vores ur til et cirkulært billedgalleri ved at erstatte <time> tags med <img> derefter opdaterer bredden (--_w) og radius (--_r) værdier:

Lad os prøve en mere. Jeg nævnte tidligere, hvordan uret lignede et moderne kunsteksperiment. Det kan vi læne os ind i og genskabe et mønster, jeg så på en plakat (som jeg desværre ikke købte) i et kunstgalleri forleden. Så vidt jeg husker, blev den kaldt "Månen" og bestod af en flok prikker, der dannede en cirkel.

En stor cirkel dannet ud af en flok mindre udfyldte cirkler i forskellige jordfarver.

Vi bruger en uordnet liste denne gang, da cirklerne ikke følger en bestemt rækkefølge. Vi kommer ikke engang til at lægge alle listepunkterne i markeringen. Lad os i stedet injicere dem med JavaScript og tilføje nogle få kontroller, vi kan bruge til at manipulere det endelige resultat.

Betjeningselementerne er områdeindgange (<input type="range">) som vi pakker ind i en <form> og lyt efter input tilfælde.

<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 kører denne metode på "input", som vil skabe en masse <li> elementer med graden (--_d) variabel, vi brugte tidligere anvendt på hver enkelt. Vi kan også genbruge vores radiusvariabel (--_r).

Jeg vil også gerne have, at prikkerne har forskellige farver. Så lad os randomisere (nå, ikke fuldstændig randomiseret) HSL-farveværdien for hvert listeelement og gem 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;
}

random() metode vælger en værdi inden for et defineret område af tal:

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

Og det er det. Vi bruger JavaScript til at gengive markeringen, men så snart det er gengivet, har vi ikke rigtig brug for det. Det sin() , cos() funktioner hjælper os med at placere alle prikkerne på de rigtige steder.

Afsluttende tanker

At placere ting omkring en cirkel er et ret grundlæggende eksempel for at demonstrere beføjelserne til trigonometriske funktioner som sin() , cos(). Men det er virkelig fedt, at vi får moderne CSS-funktioner, der giver nye løsninger til gamle løsninger. Jeg er sikker på, at vi vil se meget mere interessante, komplekse og kreative brugssager, især da browserunderstøttelse kommer til Chrome og Edge.

Tidsstempel:

Mere fra CSS-tricks