Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 45 additions & 35 deletions assets/js/moon-phase.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,13 @@
const moon = document.querySelector('[data-moon-phase]');
if (!moon) return;

const synodicMonth = 29.53058867;
const knownNewMoon = Date.UTC(2000, 0, 6, 18, 14, 0);
const now = Date.now();
const daysSince = (now - knownNewMoon) / 86400000;
const lunarAge = ((daysSince % synodicMonth) + synodicMonth) % synodicMonth;
const phase = lunarAge / synodicMonth;

let offset = 0;
let label = '满月';

if (phase < 0.125 || phase >= 0.875) {
offset = 100;
label = '新月';
} else if (phase < 0.375) {
offset = 40;
label = '上弦月';
} else if (phase < 0.625) {
offset = 0;
label = '满月';
} else {
offset = -40;
label = '下弦月';
}

moon.style.setProperty('--phase-offset', `${offset}%`);
moon.setAttribute('aria-label', label);
const text = document.querySelector('.moon-label');
if (text) text.textContent = label;

const gregorianText = document.querySelector('[data-gregorian-datetime]');
const lunarText = document.querySelector('[data-lunar-datetime]');
if (!gregorianText || !lunarText) return;
const text = document.querySelector('.moon-label');

const synodicMonth = 29.53058867;
const knownNewMoon = Date.UTC(2000, 0, 6, 18, 14, 0);

const nowDate = new Date();
const gregorianFormatter = new Intl.DateTimeFormat('en-US', {
weekday: 'short',
year: 'numeric',
Expand All @@ -53,9 +26,46 @@
});

const earthlyBranches = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
const hourIndex = Math.floor((nowDate.getHours() + 1) / 2) % 12;
const branchHour = `${earthlyBranches[hourIndex]}时`;

gregorianText.textContent = gregorianFormatter.format(nowDate);
lunarText.textContent = `${lunarFormatter.format(nowDate)} ${branchHour}`;
function getMoonPhaseLabel(phase) {
if (phase < 0.0625 || phase >= 0.9375) return '新月';
if (phase < 0.1875) return '娥眉月';
if (phase < 0.3125) return '上弦月';
if (phase < 0.4375) return '盈凸月';
if (phase < 0.5625) return '满月';
if (phase < 0.6875) return '亏凸月';
if (phase < 0.8125) return '下弦月';
return '残月';
}

function render(nowMs) {
const daysSince = (nowMs - knownNewMoon) / 86400000;
const lunarAge = ((daysSince % synodicMonth) + synodicMonth) % synodicMonth;
const phase = lunarAge / synodicMonth;

// 亮暗分界沿着朔望周期平滑变化:
// - 新月 (0.0 / 1.0) -> 0%
// - 满月 (0.5) -> ±100%
// 这样接近农历十五时月盘以白色为主。
const waxingOffset = (phase / 0.5) * 100;
const waningOffset = -((1 - phase) / 0.5) * 100;
const offset = phase < 0.5 ? waxingOffset : waningOffset;
const label = getMoonPhaseLabel(phase);

moon.style.setProperty('--phase-offset', `${offset}%`);
moon.setAttribute('aria-label', label);
if (text) text.textContent = label;

if (!gregorianText || !lunarText) return;

const nowDate = new Date(nowMs);
const hourIndex = Math.floor((nowDate.getHours() + 1) / 2) % 12;
const branchHour = `${earthlyBranches[hourIndex]}时`;

gregorianText.textContent = gregorianFormatter.format(nowDate);
lunarText.textContent = `${lunarFormatter.format(nowDate)} ${branchHour}`;
}

render(Date.now());
window.setInterval(() => render(Date.now()), 1000);
})();
Loading