LoginSignup
1

More than 3 years have passed since last update.

月齢と月の満ち欠けを表示

Last updated at Posted at 2020-12-01

JavaScriptで簡易的な月齢の計算及び月の満ち欠け表示を作成してみました。

image.jpg

動作デモ

日時及び平均朔望月からおおよその月齢を出し、月齢に応じた満ち欠けを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`;
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1