Coding and Programming

Erstellen einer Uhr mit den neuen trigonometrischen CSS-Funktionen sin() und cos() | CSS-Tipps

Written by smirow

CSS-Trigonometriefunktionen sind da! Das gilt natürlich, wenn Sie die neuesten Versionen von Firefox und Safari verwenden. Diese Art von mathematischer Leistungsfähigkeit in CSS eröffnet eine ganze Reihe von Möglichkeiten. In diesem Tutorial dachte ich, wir würden unsere Zehen ins Wasser tauchen, um ein Gefühl für einige der neuen Funktionen zu bekommen: sin() Und cos().

Es gibt weitere trigonometrische Funktionen in der Entwicklung, darunter tan() – Warum sich also nur darauf konzentrieren? sin() Und cos()? Sie passen perfekt zu meiner Idee, Text am Rand eines Kreises zu platzieren. Dies wurde hier bei CSS-Tricks behandelt, als Chris einen Ansatz mit einem Sass-Mixin teilte. Das war vor sechs Jahren, also lassen Sie es uns auf den neuesten Stand bringen.

Hier ist, was ich im Sinn habe. Auch hier wird es derzeit nur in Firefox und Safari unterstützt:

Es ist also nicht ganz so, als würden Wörter eine Kreisform bilden, sondern wir platzieren Textzeichen entlang des Kreises, um ein Zifferblatt zu bilden. Hier sind einige Tags, die wir verwenden können, um die Dinge in Gang zu bringen:

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

Als nächstes sind hier einige super grundlegende Stile für .clock-face Container. Ich habe mich für die Verwendung entschieden <time> Etikett mit 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);
}

Ich habe die Dinge dort ein wenig dekoriert, aber nur, um die Grundform und Hintergrundfarbe zu erhalten, damit wir besser sehen können, was wir tun. Beachten Sie, wie wir das speichern width Wert in einer CSS-Variablen. Wir werden es später verwenden. Im Moment gibt es nicht viel zu sehen:

Großer tomatenfarbener Kreis mit einer vertikalen Liste der Zahlen 1 bis 12 auf der linken Seite.

Klingt nach einer Art Erlebnis moderner Kunst, nicht wahr? Lassen Sie uns eine neue Variable einführen, --_rum den Kreis zu speichern Strahl, was der halben Breite des Kreises entspricht. Auf diese Weise, wenn die Breite (--_w) ändert sich, der Radiuswert (--_r) wird ebenfalls aktualisiert – dank einer weiteren CSS-Mathe-Funktion, calc():

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

Nun ein wenig Mathematik. Ein Kreis hat 360 Grad. Wir haben 12 Beschriftungen auf unserer Uhr, daher möchten wir die Zahlen alle 30 Grad platzieren (360 / 12). Im Land der Mathematik beginnt ein Kreis um 15 Uhr, also ist es eigentlich Mittag minus 90 Grad von dort aus sind es 270 Grad (360 - 90).

Fügen wir eine weitere Variable hinzu, --_dwomit wir a definieren können Grad Wert für jede Ziffer auf dem Zifferblatt. Wir erhöhen die Werte um 30 Grad, um unseren Kreis zu schließen:

.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, es ist Zeit, sich damit die Hände schmutzig zu machen sin() Und cos() Funktionen! Wir möchten diese verwenden, um die X- und Y-Koordinaten jeder Zahl zu ermitteln, damit wir sie korrekt auf dem Zifferblatt platzieren können.

Die Formel für die X-Koordinate lautet radius + (radius * cos(degree)). Lassen Sie uns dies in unser neues einbinden --_x Variable:

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

Die Formel für die Y-Koordinate lautet radius + (radius * sin(degree)). Wir haben alles, was wir brauchen, um dies zu berechnen:

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

Wir müssen einige Verwaltungsaufgaben erledigen, um die Zahlen einzurichten. Geben wir ihnen also einen grundlegenden Stil, um sicherzustellen, dass sie absolut positioniert und mit unseren Koordinaten platziert sind:

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

Beachten --_szdie wir für die verwenden werden width Und height Zahlen im Handumdrehen. Mal sehen, was wir bisher haben.

Großer tomatenfarbener Kreis mit außermittigen Stundenzahlenetiketten am Rand.

Es sieht definitiv eher wie eine Uhr aus! Sehen Sie, wie die obere linke Ecke jeder Zahl an der richtigen Stelle im Kreis positioniert ist? Wir müssen den Radius „verkleinern“, wenn wir die Positionen für jede Zahl berechnen. Wir können abziehen die Größe einer Zahl (--_sz) aus der Größe des Kreises (--_w), bevor der Radius berechnet wird:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Großer tomatenfarbener Kreis mit Stundennummernetiketten entlang der abgerundeten Kante.

Viel besser ! Lassen Sie uns die Farben ändern, um es eleganter zu machen:

Ein weißes Zifferblatt mit Zahlen auf dunkelgrauem Hintergrund.  Die Uhr hat keine Arme.

Hier könnten wir aufhören! Wir haben das Ziel erreicht, den Text um einen Kreis herum zu platzieren, oder? Aber was ist eine Uhr ohne Zeiger zur Anzeige von Stunden, Minuten und Sekunden?

Lassen Sie uns hierfür eine einzelne CSS-Animation verwenden. Fügen wir zunächst drei weitere Elemente zu unserem Markup hinzu:

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

Dann gemeinsame Markierung für die drei Arme. Auch hier kommt es vor allem darauf an, sicherzustellen, dass die Arme absolut positioniert und entsprechend platziert sind:

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

Wir werden das verwenden gleiche Animation für die drei Arme:

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

Der einzige Unterschied besteht in der Zeit, die die einzelnen Arme benötigen, um eine vollständige Umdrehung durchzuführen. Für die Stundenzeigeres braucht 12 Stunden eine komplette Drehung machen. DER animation-duration Die Eigenschaft akzeptiert nur Werte in Millisekunden und Sekunden. Bleiben wir bei den Sekunden, also 43.200 Sekunden (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Es braucht 1 Stunde für die Minutenzeiger eine komplette Drehung machen. Wir möchten jedoch, dass es sich um eine mehrstufige Animation handelt, sodass die Bewegung zwischen den Armen gestaffelt und nicht linear ist. Wir benötigen 60 Schritte, einen für jede Minute:

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

DER Sekundenzeiger Ost fast das gleiche wie der Minutenzeiger, aber die Dauer beträgt 60 Sekunden statt 60 Minuten:

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

Aktualisieren wir die Eigenschaften, die wir in den gängigen Stilen erstellt haben:

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

Was wäre, wenn wir jetzt anfangen wollten? Wir brauchen etwas 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`);

Ich fügte hinzu id="app" Zifferblatt und legen Sie zwei neue benutzerdefinierte Eigenschaften fest, die ein Negativ festlegen animation-delay, wie es Mate Marschalko tat, als er eine reine CSS-Uhr teilte. DER getHours() JavaScipt-Methode Date Das Objekt verwendet das 24-Stunden-Format, daher verwenden wir das remainder Operator, um es in das 12-Stunden-Format zu konvertieren.

Im CSS müssen wir das hinzufügen animation-delay Auch:

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

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

Eine Sache noch. Verwenden Sie CSS @supports und Eigenschaften, die wir bereits erstellt haben, können wir einen Fallback für Browser bereitstellen, die dies nicht unterstützen sin() Und cos(). (Danke 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)))
  }
}

Und los geht's! Unsere Uhr ist vorbei! Hier noch einmal die finale Demo. Auch hier wird es derzeit nur in Firefox und Safari unterstützt.

Was können wir sonst noch tun?

Ich spiele hier nur herum, aber wir können unsere Uhr schnell in eine kreisförmige Bildergalerie verwandeln, indem wir die ersetzen <time> Tags mit <img> Aktualisieren Sie dann die Breite (--_w) und der Radius (--_r) Werte:

Versuchen wir es noch einmal. Ich habe bereits erwähnt, dass die Uhr wie ein modernes Kunstexperiment aussah. Wir können uns davon inspirieren lassen und ein Muster nachbilden, das ich neulich auf einem Poster (das ich leider nicht gekauft habe) in einer Kunstgalerie gesehen habe. Wenn ich mich richtig erinnere, hieß es „Mond“ und bestand aus einer Gruppe von Punkten, die einen Kreis bildeten.

Ein großer Kreis, der aus einer Gruppe kleinerer Kreise besteht, die mit verschiedenen Erdfarben gefüllt sind.

Dieses Mal verwenden wir eine ungeordnete Liste, da die Kreise keiner bestimmten Reihenfolge folgen. Wir werden nicht einmal alle Elemente der Liste in das Markup aufnehmen. Stattdessen fügen wir ihnen JavaScript hinzu und fügen einige Steuerelemente hinzu, mit denen wir das Endergebnis manipulieren können.

Die Befehle sind Bereichseinträge (<input type="range">) was wir in ein packen werden <form> und hör es dir an input Ereignis.

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

Wir werden diese Methode auf „Eingabe“ ausführen, wodurch eine Reihe von erstellt wird <li> Elemente mit dem Diplom (--_d)-Variable, die wir zuvor auf jede angewendet haben. Wir können unsere Radiusvariable auch wiederverwenden (--_r).

Ich möchte auch, dass die Punkte unterschiedliche Farben haben. Also, lasst uns randomisieren (na ja, nicht vollständig randomisiert) den HSL-Farbwert für jedes Element in der Liste und speichert ihn als neue CSS-Variable, --_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;
}

DER random() Die Methode wählt einen Wert aus einem definierten Zahlenbereich aus:

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

Und das ist alles. Wir verwenden JavaScript, um das Markup zu rendern, aber sobald es gerendert ist, brauchen wir es nicht mehr wirklich. DER sin() Und cos() Funktionen helfen uns, alle Punkte an den richtigen Stellen zu positionieren.

Abschließende Gedanken

Das Platzieren von Objekten um einen Kreis herum ist ein ziemlich einfaches Beispiel, um die Leistungsfähigkeit trigonometrischer Funktionen zu demonstrieren sin() Und cos(). Aber es ist Wirklich Es ist cool, dass wir moderne CSS-Funktionen erhalten, die neue Lösungen für alte Problemumgehungen bieten. Ich bin mir sicher, dass wir viel interessantere, komplexere und kreativere Anwendungsfälle sehen werden, insbesondere wenn Chrome und Edge Browser-Unterstützung erhalten.

About the author

smirow

Leave a Comment