如何用JavaScript条件语句与Suncalc库实现月相每月仅显一次的实时追踪器
月相追踪器代码优化方案
问题需求
当前月相追踪器存在关键相位(新月、满月、上弦月、下弦月)重复显示的问题,需要实现以下控制逻辑:
- 新月、满月、上弦月、下弦月每月最多显示一次
- 仅在照明度为100%时显示满月,仅在照明度为0%时显示新月
- 相位顺序逻辑:
- 盈凸月 → 满月 → 亏凸月
- 残月 → 新月 → 蛾眉月
- 蛾眉月 → 上弦月 → 盈凸月
- 亏凸月 → 下弦月 → 残月
优化后的完整代码
HTML部分
<script src="https://cdnjs.cloudflare.com/ajax/libs/suncalc/1.8.0/suncalc.min.js"></script> <div class="container"> <div class="glass-box"> <img id="moon-image" src="" alt="Moon Image"> <div class="moon-info"> <p id="moon-phase-name"></p> <p id="percentage-illumination"></p> <p id="days-until-fullmoon"></p> <p id="days-until-newmoon"></p> </div> </div> </div>
JavaScript部分
// 获取当月所有关键月相的精确时间 function getCurrentMonthPhases(date) { const year = date.getFullYear(); const month = date.getMonth(); const startOfMonth = new Date(year, month, 1); const endOfMonth = new Date(year, month + 1, 1); // 获取时间段内的关键月相并按时间排序 const phases = SunCalc.getMoonPhases(startOfMonth, endOfMonth); return phases.filter(phase => phase.date < endOfMonth).sort((a, b) => a.date - b.date); } // 计算当前正确的月相 function calculateMoonPhase(date) { const illumination = SunCalc.getMoonIllumination(date); const percentage = Math.round(illumination.fraction * 100); const currentMonthPhases = getCurrentMonthPhases(date); // 严格按照明度判断新月/满月 if (percentage === 0) { return { index: 0, name: '新月' }; } else if (percentage === 100) { return { index: 4, name: '满月' }; } // 根据关键月相区间判断当前过渡相位 for (let i = 0; i < currentMonthPhases.length; i++) { const currentPhase = currentMonthPhases[i]; const nextPhase = currentMonthPhases[i + 1] || getCurrentMonthPhases(new Date(date.getFullYear(), date.getMonth() + 1, 1))[0]; if (date >= currentPhase.date && date < nextPhase.date) { // 根据当前相位判断后续过渡相 switch (currentPhase.phase) { case 'new': return { index: 1, name: '蛾眉月' }; // 新月后 case 'firstQuarter': return { index: 3, name: '盈凸月' }; // 上弦月后 case 'full': return { index: 5, name: '亏凸月' }; // 满月后 case 'lastQuarter': return { index: 7, name: '残月' }; // 下弦月后 } // 根据下一相位判断前置过渡相 switch (nextPhase.phase) { case 'new': return { index: 7, name: '残月' }; // 新月前 case 'firstQuarter': return { index: 1, name: '蛾眉月' }; // 上弦月前 case 'full': return { index: 3, name: '盈凸月' }; // 满月前 case 'lastQuarter': return { index: 5, name: '亏凸月' }; // 下弦月前 } } } // 极端情况的 fallback 计算 const phaseAngle = illumination.phase; if (phaseAngle < 0.125) return { index: 1, name: '蛾眉月' }; if (phaseAngle < 0.25) return { index: 2, name: '上弦月' }; if (phaseAngle < 0.375) return { index: 3, name: '盈凸月' }; if (phaseAngle < 0.625) return { index: 5, name: '亏凸月' }; if (phaseAngle < 0.75) return { index: 6, name: '下弦月' }; return { index: 7, name: '残月' }; } // 计算照明度及到下一次关键月相的天数 function calculateMoonIllumination(date) { const illumination = SunCalc.getMoonIllumination(date); const percentage = Math.round(illumination.fraction * 100); // 获取下一次满月和新月的精确时间 const nextPhaseRange = new Date(date.getTime() + 30 * 86400000); const nextFullMoon = SunCalc.getMoonPhases(date, nextPhaseRange).find(p => p.phase === 'full').date; const nextNewMoon = SunCalc.getMoonPhases(date, nextPhaseRange).find(p => p.phase === 'new').date; const daysUntilFullMoon = Math.round((nextFullMoon - date) / (1000 * 60 * 60 * 24)); const daysUntilNewMoon = Math.round((nextNewMoon - date) / (1000 * 60 * 60 * 24)); return { percentage: percentage, daysUntilFullMoon: daysUntilFullMoon, daysUntilNewMoon: daysUntilNewMoon }; } // 更新页面显示元素 function updateMoonPhaseElements(moonPhase, daysUntilFullMoon, daysUntilNewMoon, percentageIllumination) { const moonPhaseImageURL = `images/${moonPhase.index}.jpg`; const moonPhaseImage = document.getElementById('moon-image'); const moonPhaseName = document.getElementById('moon-phase-name'); const daysUntilFullMoonElement = document.getElementById('days-until-fullmoon'); const daysUntilNewMoonElement = document.getElementById('days-until-newmoon'); const percentageIlluminationElement = document.getElementById('percentage-illumination'); moonPhaseImage.src = moonPhaseImageURL; moonPhaseName.textContent = moonPhase.name; daysUntilFullMoonElement.textContent = `距离满月还有:${daysUntilFullMoon}天`; daysUntilNewMoonElement.textContent = `距离新月还有:${daysUntilNewMoon}天`; percentageIlluminationElement.textContent = `月面照明度:${percentageIllumination}%`; } // 月相更新主函数 function updateMoonPhase() { try { const today = new Date(); const moonPhase = calculateMoonPhase(today); const illuminationData = calculateMoonIllumination(today); updateMoonPhaseElements( moonPhase, illuminationData.daysUntilFullMoon, illuminationData.daysUntilNewMoon, illuminationData.percentage ); } catch (error) { console.error('月相更新失败:', error); } } // 初始化更新 updateMoonPhase(); // 每日自动更新 setInterval(updateMoonPhase, 86400000);
优化关键点说明
- 精确相位获取:使用
SunCalc.getMoonPhases获取当月关键相位的精确时间,替代原有的平均月相近似计算,确保每个关键相位每月仅出现一次。 - 照明度校验:严格判断照明度为0%时才显示新月,100%时显示满月,其余情况归为相邻的过渡相位。
- 相位顺序控制:通过判断当前日期在关键相位区间中的位置,严格遵循需求中的相位递进关系。
- 跨月处理:自动处理跨月相位的情况,保证月相判断的连续性。
- 实时更新:添加每日自动更新逻辑,确保显示内容的时效性。
内容的提问来源于stack exchange,提问作者mentos
相关产品推荐
相关产品推荐

