You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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);

优化关键点说明

  1. 精确相位获取:使用SunCalc.getMoonPhases获取当月关键相位的精确时间,替代原有的平均月相近似计算,确保每个关键相位每月仅出现一次。
  2. 照明度校验:严格判断照明度为0%时才显示新月,100%时显示满月,其余情况归为相邻的过渡相位。
  3. 相位顺序控制:通过判断当前日期在关键相位区间中的位置,严格遵循需求中的相位递进关系。
  4. 跨月处理:自动处理跨月相位的情况,保证月相判断的连续性。
  5. 实时更新:添加每日自动更新逻辑,确保显示内容的时效性。

内容的提问来源于stack exchange,提问作者mentos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 00:57:02