Moon phase calculator and visualizer
by thormeier
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>
CSS
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;
}
.sphere {
border-radius: 100%;
width: 250px;
height: 250px;
display: flex;
overflow: hidden;
align-items: center;
position: relative;
margin-bottom: 50px;
}
.hemisphere {
width: 50%;
height: 100%;
}
.light {
background-color: #F4F6F0;
}
.dark {
background-color: #575851;
}
.divider,
.divider:after {
top: 0;
left: 0;
width: 250px;
height: 250px;
position: absolute;
border-radius: 100%;
transform-style: preserve-3d;
backface-visibility: hidden;
}
.divider {
background-color: #575851;
}
.divider:after {
content: '';
background-color: #F4F6F0;
transform: rotateY(180deg);
}
JavaScript
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)
}
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')
hemispheres[1].classList.add('dark')
hemispheres[1].classList.remove('light')
} else {
hemispheres[0].classList.add('dark')
hemispheres[0].classList.remove('light')
hemispheres[1].classList.remove('dark')
hemispheres[1].classList.add('light')
}
}
const setMoonTitle = date => {
document.querySelector('#date-title').innerHTML = `Moon phase for ${date.toUTCString()}`
}
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))
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
`
}
document.body.style.background = [...Array(100)].map(() => getStar()).join(', ')