JavaScriptで簡易的な月齢の計算及び月の満ち欠け表示を作成してみました。
日時及び平均朔望月からおおよその月齢を出し、月齢に応じた満ち欠けをcanvasを使用して描画しています。
満ち欠けの状態に応じた形をその都度描いているわけではなく、新月(暗 真円)、上弦及び下弦(明 半円)、リサイズ用真円(明暗切替)の3枚を重ね合わせて、CSSで反転や変形させることで表現しています。
言葉では伝わりにくいかと思いますが、こちらの満ち欠けテストページで適当に操作していただければ分かりやすいかもしれません。
index.html
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta name="viewport" content="user-scalable=no,width=device-width,initial-scale=1">
<meta charset='utf-8'>
<title>月齢表示</title>
<link rel='stylesheet' href='./style.css' type='text/css'>
<script src='./script.js'></script>
</head>
<body>
<div class='d'>
<div class='moonAge'>
<canvas id='a0'></canvas>
<canvas id='a1'></canvas>
<canvas id='a2'></canvas>
</div>
<div id='disp'>
</div>
</div>
<input type='date' id='c' onchange='chg(this.value)'>
</body>
</html>
style.css
.d {
background: black;
width: 150px;
}
#disp {
color: #ffff00;
}
.moonAge {
position: relative;
height: 200px;
}
canvas[id*='a'] {
position: absolute;
top: 0px;
left: 0px;
}
script.js
'use strict';
const size = 200;
const
pi = Math.PI,
pi2 = pi * 2,
topAngle = pi + pi / 2 * 3,
bottomAngle = pi + pi / 2,
halfSize = size / 2,
c = [],
ctx = [],
start = [0, topAngle, 0],
end = [pi2, bottomAngle, pi2];
window.addEventListener('DOMContentLoaded', function(){
document.querySelector('.moonAge').style.height = `${size}px`;
document.querySelector('.d').style.width = `${size}px`;
for(let i = 0; i < 3; i++) {
c[i] = document.getElementById(`a${i}`);
c[i].style.width = `${size}px`;
c[i].style.height = `${size}px`;
c[i].width = size;
c[i].height = size;
ctx[i] = c[i].getContext('2d');
ctx[i].fillStyle = i === 0 ? '#444444' : '#ffff00';
ctx[i].arc(halfSize, halfSize, halfSize * .95, start[i], end[i]);
ctx[i].fill();
}
const e = document.querySelector('#c');
e.value = new Date().toLocaleDateString('sv');
chg(e.value);
}, false);
function chg(d){
const date = new Date(d);
if(isNaN(date.getTime())) return;
date.setHours(12);
const day = date.getTime() / 864e5 - 6.475,
// 平均朔望月
r = 29.530588853 +
2.162e-9 * ((date.getTime() - 946727935816) / 315576e5),
age = day > 0 ? day % r : (r + day % r) % r;
document.querySelector('#disp').innerHTML =
`${date.toLocaleDateString()}<br>月齢:${age.toFixed(1)}`;
appearance(age, r);
}
function appearance(age, m) {
const s = Math.cos(pi2 * age / m),
s2 = Math.sin(pi2 * age / m),
r = Math.abs(halfSize * s);
c[1].style.transform = `rotate(${s2 > 0 ? 180 : 0}deg)`;
ctx[2].clearRect(0, 0, size, size);
ctx[2].beginPath();
ctx[2].fillStyle = s > 0 ? '#444444' : '#ffff00';
ctx[2].arc(halfSize, halfSize, halfSize * .95, 0, pi2);
ctx[2].fill();
c[2].style.width = `${r * 2}px`;
c[2].style.left = `${halfSize - r}px`;
}