JavaScript >> Javascript-Tutorial >  >> Tags >> CSS

Verwenden Sie Ihre i-moon-gination:Lassen Sie uns einen Mondphasen-Visualizer mit CSS und JS erstellen! 🗓️🌙

Titelbild von Flickr-Nutzer Brendan Keene

Ah, es ist Frühling auf der Nordhalbkugel! Die Nächte werden wieder wärmer (und kürzer!), keine Wolken in Sicht, die perfekte Zeit zum Mondgucken, oder? Ich hatte schon immer eine große Faszination für unseren größten natürlichen Satelliten und eigentlich für den Nachthimmel im Allgemeinen.

Lassen Sie uns heute etwas tiefer in die Mondphasen eintauchen und unseren eigenen Mondphasenrechner und -visualisierer erstellen!

Wie funktionieren Mondphasen überhaupt?

Ich bin keineswegs ein Experte der Orbitalmechanik, geschweige denn der meisten Mathematik, die für die Orbitalmechanik erforderlich sind, aber ich werde versuchen, es trotzdem zu erklären. Die Tatsache, dass ich es sogar weiß Was Orbitalmechanik ist, ist mir immer noch ein Rätsel.

Wie Sie vielleicht wissen, dreht sich der Mond um die Erde und die Erde um die Sonne. [Zitat erforderlich]

Die Erde dreht sich ungefähr alle 12 Monate um die Sonne, plus oder minus ein paar Minuten, dafür sind Schaltjahre da. Der Mond braucht etwa 27,3 Tage, um die Erde einmal zu umkreisen. Irgendwann in der Vergangenheit verlangsamte die Schwerkraft der Erde die Rotation des Mondes bis zu dem Punkt, an dem die Umlaufbahn des Mondes um die Erde mit seiner eigenen Rotation übereinstimmte. Der Mond wurde gezeitengesperrt. Das bedeutet, dass es immer auf die gleiche Seite der Erde zeigt.

Das bedeutet jedoch nicht, dass der Mond stationär ist. Sie dreht sich um die Erde und die Erde dreht sich immer noch um die Sonne. In seltenen Fällen stehen Erde, Sonne und Mond auf einer Linie:Dann kommt es zu einer Sonnenfinsternis (Mond zwischen Erde und Sonne) oder einer Mondfinsternis (Erde zwischen Sonne und Mond).

Wenn das nicht ist passiert (also eigentlich meistens), wird der Mond von der Sonne beleuchtet. Wenn sich der Mond/Erde-Winkel ändert, werden verschiedene Seiten des Mondes beleuchtet. Daraus ergeben sich die unterschiedlichen Mondphasen.

Wikipedia-Benutzer Andonee hat dies auf großartige Weise illustriert:

Man sieht ziemlich genau, wie es funktioniert:Der Mond wird immer irgendwie beleuchtet, aber der Winkel, von welcher Seite er beleuchtet wird, ändert sich, was zu den Mondphasen führt, die wir sehen. Jeder Zyklus dauert etwa 29,5 Tage. Also, 29,5 Tage =360 Grad Drehung. 29,5 ist nicht 27,3 und das ist genau der Punkt, an dem die Mathematik komplex wird. Ich habs. Lassen Sie uns codieren.

Kesselpanzerung!

Es wäre großartig, eine Datumsauswahl zu haben, um verschiedene Daten zu überprüfen. Das aktuell ausgewählte Datum sollte angezeigt werden. Und wir brauchen – nun ja – einen Mond. Der Mond hat eine helle und eine dunkle Halbkugel und einen Teiler. Beginnen wir zuerst mit dem HTML:

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="styles.css">
</head>

<h1 id="date-title">
  <!-- Will show the selected date -->
</h1>

<!-- The moon -->
<div class="sphere">
  <div class="light hemisphere"></div>
  <div class="dark hemisphere"></div>
  <div class="divider"></div>
</div>

<!-- The date input -->
<input type="date">

<script src="app.js"></script>
</html>

Ich habe auch eine leere JS-Datei und eine leere CSS-Datei hinzugefügt.

Kommen wir zum Styling.

Schön machen

Wir beginnen mit dem Hintergrund. Wir verwenden Flexbox, um alles zu zentrieren. Der Titel sollte eine schöne, helle Farbe haben, damit er auf dem dunkelblauen Hintergrund gut sichtbar ist.

html {
    background-color: rgba(11,14,58,1);
    overflow: hidden;
}

body {
    text-align: center;
    width: 100vw;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

h1 {
    color: #F4F6F0;
    margin-bottom: 50px;
}

Als nächstes lassen wir den Mond (Achtung, schlechtes Wortspiel!) sich drehen:

.sphere {
    border-radius: 100%;
    width: 300px;
    height: 300px;
    overflow: hidden;
    display: flex;
    align-items: center;
    position: relative;
    margin-bottom: 50px;
}

Sie haben vielleicht bemerkt, dass wir auch hier Flexbox verwenden. Wir brauchen die beiden Halbkugeln nebeneinander, damit der Teiler funktioniert.

.hemisphere {
    width: 50%;
    height: 100%;
}

.light {
    background-color: #F4F6F0;
}

.dark {
    background-color: #575851;
}

Schließlich brauchen wir den Teiler. Um eine echte Kugel zu simulieren, gestalten wir die Trennlinie als Kreis und drehen sie im 3D-Raum. Da sich der Mond um 360 Grad dreht, sollte sich auch der Teiler um 360 Grad drehen können. Die Trennwand braucht also zwei Seiten:Eine helle Seite und eine dunkle Seite. Wir verwenden den :after des Teilers Pseudo-Element dafür und drehen Sie es um 180 Grad auf der Y-Achse, um als Rückseite des Teilers zu dienen:

.divider,
.divider:after {
    top: 0;
    left: 0;
    width: 300px;
    height: 300px;
    position: absolute;
    border-radius: 100%;
    transform-style: preserve-3d;
    backface-visibility: hidden;
}

.divider {
    background-color: #575851; /* Dark */
}

.divider:after {
    content: '';
    background-color: #F4F6F0; /* Light */
    transform: rotateY(180deg);
}

Die Mondphase anzeigen lassen

Um zu wissen, wie weit sich der Mond gerade in der Phase befindet, müssen wir einen Zeitpunkt in der Vergangenheit eines Neumonds kennen, also einen völlig dunklen. Ein solcher Anlass war der 2. März 2022 um 18:34 Uhr UTC+1.

Eine Mondphase dauert ungefähr 29,5 Tage und wir müssen den Teiler um 0-360 Grad drehen. Um also die Rotation an einem bestimmten Datum zu erhalten, können wir die Differenz zwischen dem gewählten Datum und dem 2. März nehmen, die Anzahl der Tage berechnen, ein beliebiges Vielfaches von 29,5 subtrahieren, diesen Rest durch 29,5 dividieren und mit 360 multiplizieren. Dann müssen wir subtrahieren Sie das von 360, um es an unseren Teiler und die Funktionsweise der CSS-Rotation anzupassen:

const getMoonPhaseRotation = date => {
  const cycleLength = 29.5 // days

  const knownNewMoon = new Date('2022-03-02 18:34:00')
  const secondsSinceKnownNewMoon = (date - knownNewMoon) / 1000
  const daysSinceKnownNewMoon = secondsSinceKnownNewMoon / 60 / 60 / 24
  const currentMoonPhasePercentage = (daysSinceKnownNewMoon % cycleLength) / cycleLength

  return 360 - Math.floor(currentMoonPhasePercentage * 360)
}

Da nun die Drehung der Scheibe nicht automatisch die richtige Hemisphäre überlappt (diese sind immer noch hell und dunkel), brauchen wir die helle und die dunkle Hemisphäre, um die Plätze zu tauschen, damit es tatsächlich aussieht als würde sich der beleuchtete Teil bewegen. Dazu schalten wir einige Klassen basierend auf der berechneten Rotation um. Wir wenden dann auch das Styling für die Drehung des Teilers an, et voila, ein funktionierender Mondphasen-Visualizer.

const setMoonRotation = deg => {
  document.querySelector('.divider').style.transform = `rotate3d(0, 1, 0, ${deg}deg)`

  const hemispheres = document.querySelectorAll('.hemisphere')

  if (deg < 180) {
    // Left
    hemispheres[0].classList.remove('dark')
    hemispheres[0].classList.add('light')

    // Right
    hemispheres[1].classList.add('dark')
    hemispheres[1].classList.remove('light')
  } else {
    // Left
    hemispheres[0].classList.add('dark')
    hemispheres[0].classList.remove('light')

    // Right
    hemispheres[1].classList.remove('dark')
    hemispheres[1].classList.add('light')
  }
}

Zuletzt fügen wir eine Funktion hinzu, um den Titel zu aktualisieren:

const setMoonTitle = date => {
  document.querySelector('#date-title').innerHTML = `Moon phase for ${date.toUTCString()}`
}

Dinge zusammenbinden

Lassen Sie uns nun diese Funktionen miteinander arbeiten lassen:

const today = new Date()
const dateSelect = document.querySelector('input')

dateSelect.addEventListener('input', e => {
  const selectedDate = new Date(e.target.value)

  setMoonTitle(selectedDate)
  setMoonRotation(getMoonPhaseRotation(selectedDate))
})

dateSelect.value = today.toISOString().slice(0, 10)

setMoonTitle(today)
setMoonRotation(getMoonPhaseRotation(today))

Großartig!

Bonus:Funkel, Funkel, kleiner Stern

Ein paar Sterne um unseren Mond wären auch schön, oder? Ja? Kühl. Nehmen wir eine Tonne von linearen Gradienten. Jeder lineare Verlauf hat einen hellen Fleck, der in die Hintergrundfarbe des HTML-Codes übergeht und dann transparent wird. Auf diese Weise "überlappen" sie sich nicht und wir brauchen nicht tonnenweise zusätzliche Elemente.

Mal sehen, was ich mit einer Funktion zum Generieren des Farbverlaufs für einen einzelnen Stern meine:

const getStar = () => {
  const x = Math.round(Math.random() * 100)
  const y = Math.round(Math.random() * 100)

  return `
    radial-gradient(circle at ${x}% ${y}%, 
    rgba(255,255,255,1) 0%, 
    rgba(11,14,58,1) 3px, 
    rgba(11,14,58,0) 5px, 
    rgba(11,14,58,0) 100%) no-repeat border-box
  `
}

Wie Sie sehen können, geht der Stern selbst von #ffffff aus bis rgba(11,14,58,1) für 3 Pixel und wird für weitere 2 Pixel transparent. Der Rest dieses Farbverlaufs ist transparent. Wenn Sie mehrere Farbverläufe kombinieren, "gewinnt" der zuletzt hinzugefügte und überlappt alle anderen. Wenn Teile dieses Farbverlaufs transparent sind, kann der Hintergrund durchscheinen (haha). Indem wir den größten Teil des linearen Farbverlaufs transparent machen, können wir viele davon verteilen, wo immer wir wollen.

Jetzt müssen wir tatsächlich viele Sterne generieren und sie dem Körper hinzufügen:

document.body.style.background = [...Array(100)].map(() => getStar()).join(', ')

Uuund fertig!

Demozeit!

(Klicken Sie auf "Ergebnis", um es in Aktion zu sehen)

Ich kann es kaum erwarten, zu überprüfen, ob die Berechnungen stimmen! Ich hoffe, wir haben heute Nacht eine klare Nacht!

Ich hoffe, Sie haben es genauso genossen, diesen Artikel zu lesen, wie ich es genossen habe, ihn zu schreiben! Wenn ja, hinterlassen Sie ein ❤️ oder ein 🦄! In meiner Freizeit schreibe ich Technikartikel und trinke ab und zu gerne Kaffee.

Wenn Sie meine Bemühungen unterstützen möchten, Du kannst mir einen Kaffee anbietenoder Folgen Sie mir auf Twitter 🐦! Du kannst mich auch direkt über Paypal unterstützen!