跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python大前端算法

Python 实现月相计算与可视化效果展示

基于 Python 的月相计算与可视化系统,涵盖朔望月周期算法、Matplotlib 绘图及 Web 界面生成。通过时间轴、曲线图等多种图表展示月相变化,结合 CSS3 与 JavaScript 实现星空流星特效。适合学习天文数据可视化与前端交互开发。

Eee_123发布于 2026/3/15更新于 2026/8/2451 浏览
Python 实现月相计算与可视化效果展示

引言

中秋节承载着团圆与和谐的文化寓意。我们不妨用 Python 来创造一个富有诗意的月相可视化器,通过代码重现天空中月亮的盈亏变化。

月相可视化效果

项目概述

我们将构建一个功能丰富的月相可视化系统,主要包含以下特性:

  • 精确的天文计算:基于 29.53 天月相周期的高精度算法
  • Web 界面生成:自动生成华丽的 HTML 可视化界面
  • 视觉特效丰富:星空背景、流星效果、月亮光晕等
  • 多维度展示:时间轴、曲线图、年度概览等四种图表
  • 中秋文化融入:诗词展示、传统装饰元素

技术架构

首先安装必要的依赖库:

pip install numpy matplotlib datetime base64 io warnings

项目结构

moon-phase-visualizer/
├── moon_calculator.py      # 核心计算引擎
├── generate_html.py        # HTML 生成器
└── moon_phase_2025_mid_autumn.html  # 生成的界面

实现思路

月相计算核心

月相变化周期约为 29.53 天。选定 2025 年 9 月 25 日作为参考新月,通过计算目标日期与参考点的时间差,结合朔望月周期进行数学建模。

算法的关键是将连续时间变化映射到 0-1 的月相值:前半周期(新月→满月),后半周期(满月→新月)。

可视化难点

Matplotlib 中文显示问题通过多字体回退解决:微软雅黑 → 黑体 → 其他系统字体。 月亮绘制用圆形加椭圆阴影实现,阴影宽度根据月相值动态计算。中秋节特效包括多层光晕和星星装饰。

核心模块设计

moon_calculator.py 核心计算引擎
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from datetime import datetime, timedelta
import math
import base64
import io
import warnings
from matplotlib import rcParams

warnings.filterwarnings('ignore')
# 设置 matplotlib 为非交互式后端
plt.switch_backend('Agg')
# 优化中文字体设置
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

class MoonPhaseCalculator:
    """月相计算器类"""
    def __init__(self):
        # 月相周期约为 29.53 天
        self.lunar_cycle = 29.530588853
        # 更新参考新月日期(2025 年 9 月 25 日新月)
        self.reference_new_moon = datetime(2025, 9, 25)

    def get_moon_phase(self, date):
        """计算指定日期的月相"""
        days_since_new_moon = (date - self.reference_new_moon).total_seconds() / (24 * 3600)
        cycle_position = (days_since_new_moon % self.lunar_cycle) / self.lunar_cycle
        if cycle_position <= 0.5:
            phase = cycle_position * 2
        else:
            phase = 2 - (cycle_position * 2)
        return phase

    def get_moon_age(self, date):
        """计算月龄"""
        days_since_new_moon = (date - self.reference_new_moon).total_seconds() / (24 * 3600)
        return days_since_new_moon % self.lunar_cycle

    def is_mid_autumn_festival(self, date):
        """判断是否为中秋节(2025 年 10 月 6 日)"""
        mid_autumn_2025 = datetime(2025, 10, 6)
        return abs((date - mid_autumn_2025).days) == 0

    def get_mid_autumn_date(self, year=2025):
        """获取指定年份的中秋节日期"""
        if year == 2025:
            return datetime(2025, 10, 6)
        elif year == 2024:
            return datetime(2024, 9, 17)
        elif year == 2026:
            return datetime(2026, 9, 25)
        else:
            return datetime(2025, 10, 6)

核心算法:

  • lunar_cycle = 29.530588853:朔望月精确天数
  • get_moon_phase() 通过时间差和模运算计算月相值
  • 使用 total_seconds() 确保计算精度
可视化渲染类
class WebMoonVisualizer:
    """Web 版月相可视化器"""
    def __init__(self):
        self.calculator = MoonPhaseCalculator()

    def draw_moon(self, ax, phase, size=1.0, position=(0, 0), is_mid_autumn=False):
        """绘制月亮形状"""
        x, y = position
        # 绘制月亮基础圆形
        circle = patches.Circle((x, y), size, facecolor='lightyellow', edgecolor='gold', linewidth=2)
        ax.add_patch(circle)
        # 根据月相绘制阴影部分
        if phase < 0.95:
            shadow_width = size * 2 * (1 - phase)
            if shadow_width > 0.1:
                shadow = patches.Ellipse((x + size * 0.1, y), shadow_width, size * 2, facecolor='darkgray', alpha=0.6)
                ax.add_patch(shadow)
        # 中秋节特效
        if is_mid_autumn:
            for i in range(4):
                halo = patches.Circle((x, y), size * (1.3 + i * 0.15), facecolor='orange', alpha=0.12 - i * 0.03, edgecolor='none')
                ax.add_patch(halo)
            # 添加星星装饰
            star_positions = [(x - size * 2.0, y + size * 1.0), (x + size * 2.0, y + size * 1.0),
                              (x - size * 1.8, y - size * 1.3), (x + size * 1.8, y - size * 1.3)]
            for sx, sy in star_positions:
                star = patches.RegularPolygon((sx, sy), 5, radius=size * 0.12, facecolor='gold', alpha=0.9)
                ax.add_patch(star)

    def get_phase_name(self, phase):
        """获取月相名称"""
        if phase < 0.1:
            return "新月"
        elif phase < 0.35:
            return "蛾眉月"
        elif phase < 0.65:
            return "上弦月"
        elif phase < 0.9:
            return "盈凸月"
        else:
            return "满月"

绘制要点:

  • 用 patches.Circle 绘制月亮基础形状
  • 椭圆阴影模拟月相变化:shadow_width = size * 2 * (1 - phase)
  • 中秋特效:多层光晕 + 五角星装饰
  • 颜色搭配:淡黄底色 + 金色边框

月相绘制效果

图表实现详解

时间轴图表

def create_timeline_chart(self, center_date=None, days=30):
    """创建时间轴图表(修复重叠问题)"""
    if center_date is None:
        center_date = datetime(2025, 10, 6)
    start_date = center_date - timedelta(days=15)
    fig, ax = plt.subplots(figsize=(24, 12))
    for i in range(days):
        current_date = start_date + timedelta(days=i)
        phase = self.calculator.get_moon_phase(current_date)
        is_mid_autumn = self.calculator.is_mid_autumn_festival(current_date)
        x_pos = i * 3.5  # 增大间距避免重叠
        y_pos = 0
        self.draw_moon(ax, phase, size=1.0, position=(x_pos, y_pos), is_mid_autumn=is_mid_autumn)
        # 添加日期标签
        ax.text(x_pos, -3.0, current_date.strftime('%m-%d'), ha='center', va='top', fontsize=11, fontweight='bold')
        # 中秋节特殊标记
        if is_mid_autumn:
            ax.text(x_pos, 3.5, '2025 年中秋节', ha='center', va='bottom', fontsize=14, fontweight='bold', color='red')

实现关键:

  • x_pos = i * 3.5:间距设置避免重叠
  • 24x12 大画布确保显示完整
  • 中秋节用红色高亮 + 边框装饰

时间轴图表

月相曲线图

def create_phase_chart(self):
    """创建月相图表"""
    fig, ax = plt.subplots(figsize=(12, 8))
    ax.set_facecolor('#001133')
    today = datetime(2025, 10, 6)
    dates_range = [today + timedelta(days=i - 15) for i in range(31)]
    phase_values = [self.calculator.get_moon_phase(d) for d in dates_range]
    ax.plot(range(31), phase_values, 'gold', linewidth=4, marker='o', markersize=6, markerfacecolor='yellow', markeredgecolor='orange')
    ax.fill_between(range(31), phase_values, alpha=0.3, color='gold')
    # 重要标记线
    ax.axhline(y=1.0, color='red', linestyle='--', alpha=0.8, linewidth=2, label='Full Moon')
    ax.axhline(y=0.0, color='silver', linestyle='--', alpha=0.8, linewidth=2, label='New Moon')
    ax.axvline(x=15, color='lime', linestyle=':', alpha=0.8, linewidth=3, label='Mid-Autumn Festival')
    mid_autumn_phase = self.calculator.get_moon_phase(today)
    ax.plot(15, mid_autumn_phase, 'r*', markersize=15, label=f'Festival Phase({mid_autumn_phase:.2f})')

设计要点:

  • 深蓝背景 + 金色曲线营造夜空效果
  • fill_between 创建填充区域增强视觉效果
  • 水平/垂直参考线标注关键点
  • 红色星标突出中秋节位置

月相曲线图

当前月相图

def create_current_moon(self):
    """创建当前月相图"""
    fig, ax = plt.subplots(figsize=(8, 8))
    ax.set_facecolor('#001133')
    today = datetime(2025, 10, 6)
    phase_today = self.calculator.get_moon_phase(today)
    is_today_mid_autumn = self.calculator.is_mid_autumn_festival(today)
    self.draw_moon(ax, phase_today, size=3.0, position=(0, 0), is_mid_autumn=is_today_mid_autumn)
    ax.set_xlim(-6, 6)
    ax.set_ylim(-6, 6)
    ax.set_aspect('equal')
    ax.axis('off')
    phase_name = self.get_phase_name(phase_today)
    title = f'2025 Mid-Autumn Festival Moon Phase - {phase_name}\n{today.strftime("%Y-%m-%d")}\nPhase Value: {phase_today:.3f}'
    if is_today_mid_autumn:
        title += '\nHappy Mid-Autumn Festival!'
    ax.text(0, -5, title, ha='center', va='top', fontsize=14, color='white', fontweight='bold', bbox=dict(boxstyle="round,pad=0.8", facecolor="darkblue", alpha=0.8))

技术特点:

  • 8x8 正方形画布保证月亮圆形显示
  • size=3.0 大尺寸突出视觉效果
  • 圆角文本框显示详细信息

当前月相图

图像 Base64 编码

def fig_to_base64(self, fig):
    """将 matplotlib 图形转换为 base64 字符串"""
    buffer = io.BytesIO()
    fig.savefig(buffer, format='png', facecolor=fig.get_facecolor(), bbox_inches='tight', dpi=150)
    buffer.seek(0)
    image_png = buffer.getvalue()
    buffer.close()
    plt.close(fig)
    graphic = base64.b64encode(image_png)
    return graphic.decode('utf-8')

要点:

  • 内存缓冲区避免临时文件
  • dpi=150 平衡质量与大小
  • plt.close(fig) 释放内存防止泄漏

HTML 界面生成

generate_html.py 界面组装器

from moon_calculator import WebMoonVisualizer
from datetime import datetime

def generate_html():
    visualizer = WebMoonVisualizer()
    # 生成图表
    mid_autumn_date = datetime(2025, 10, 6)
    timeline_fig = visualizer.create_timeline_chart(mid_autumn_date, days=30)
    timeline_img = visualizer.fig_to_base64(timeline_fig)
    phase_fig = visualizer.create_phase_chart()
    phase_img = visualizer.fig_to_base64(phase_fig)
    current_fig = visualizer.create_current_moon()
    current_img = visualizer.fig_to_base64(current_fig)
    annual_fig = visualizer.create_annual_overview()
    annual_img = visualizer.fig_to_base64(annual_fig)
    # 获取月相信息
    moon_info = visualizer.get_moon_info()

CSS3 特效设计

/* 星空背景动画 */
@keyframes twinkle {
    0%, 100% { opacity: 0.3; }
    50% { opacity: 1; }
}
/* 流星效果 */
@keyframes shooting {
    0% { transform: rotate(-45deg) translateX(0) translateY(0); opacity: 1; }
    100% { transform: rotate(-45deg) translateX(-800px) translateY(800px); opacity: 0; }
}
/* 月亮背景浮动 */
@keyframes float {
    0%, 100% { transform: translateY(0px); }
    50% { transform: translateY(-15px); }
}
/* 渐变文字效果 */
.highlight {
    background: linear-gradient(45deg, #ff6b35, #f7931e, #ffd700);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: gradient-shift 3s ease infinite;
}

CSS 特效演示

JavaScript 交互特效

// 创建 120 颗星星
function createStars() {
    const starsContainer = document.getElementById('stars');
    const numberOfStars = 120;
    for (let i = 0; i < numberOfStars; i++) {
        const star = document.createElement('div');
        star.className = 'star';
        star.style.left = Math.random() * 100 + '%';
        star.style.top = Math.random() * 100 + '%';
        const size = Math.random() * 3 + 1;
        star.style.width = size + 'px';
        star.style.height = size + 'px';
        star.style.animationDelay = Math.random() * 3 + 's';
        starsContainer.appendChild(star);
    }
}
// 流星效果
function createShootingStar() {
    const shootingStar = document.createElement('div');
    shootingStar.className = 'shooting-star';
    const startX = Math.random() * window.innerWidth;
    const startY = Math.random() * (window.innerHeight * 0.5);
    shootingStar.style.left = startX + 'px';
    shootingStar.style.top = startY + 'px';
    shootingStar.style.animation = 'shooting 1.5s linear forwards';
    document.body.appendChild(shootingStar);
    setTimeout(() => {
        shootingStar.remove();
    }, 1500);
}

特效实现:

  • 120 颗随机分布的闪烁星星
  • 流星从随机位置斜向划过
  • 自动清理 DOM 防止内存泄漏

结语

愿我们的代码也能像这轮明月一样,在技术的夜空中永远闪耀着智慧的光芒,连接着传统与未来,架起文化与科技的桥梁。

目录

  1. 引言
  2. 项目概述
  3. 技术架构
  4. 项目结构
  5. 实现思路
  6. 月相计算核心
  7. 可视化难点
  8. 核心模块设计
  9. moon_calculator.py 核心计算引擎
  10. 设置 matplotlib 为非交互式后端
  11. 优化中文字体设置
  12. 可视化渲染类
  13. 图表实现详解
  14. 时间轴图表
  15. 月相曲线图
  16. 当前月相图
  17. 图像 Base64 编码
  18. HTML 界面生成
  19. generate_html.py 界面组装器
  20. CSS3 特效设计
  21. JavaScript 交互特效
  22. 结语
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 实现 CSV 数据导入 Neo4j
  • HTML 入门:结构、常用标签与 HTML5 要点
  • GitHub Copilot 学生身份认证与激活完整指南
  • JDK 核心实操指南:从安装配置到项目打包调试全流程
  • Llama 3-8B-Instruct 在昇腾 NPU 上的 SGLang 性能实测
  • 基于 DeepSeek 与腾讯云 HAI 快速构建个人主页
  • AI 绘画入门:Stable Diffusion XL 部署与使用指南
  • 数据结构:C 语言单链表实现与原理
  • Python 使用 Selenium 实现电商自动抢购脚本教程
  • SpringBoot 整合 Neo4j 图数据库实战详解
  • 环形房屋抢劫问题:动态规划解题逻辑与技巧
  • SkyWalking 全链路监控实战:Spring Cloud、Dubbo、RocketMQ 集成指南
  • 即梦 AI 基础操作指南:绘画与视频生成
  • Openclaw 与 Scrapling 手动部署及 Walmart 数据采集策略
  • 深入理解 C++ STL 优先队列 priority_queue 原理与实现
  • 使用 OVITO-Python 进行 LAMMPS 后处理:统计 Type=1 原子沿 X 方向密度分布
  • HDFS 读写究竟做了什么?从 Java 代码看数据流内幕
  • SDIO 控制器详解:从架构到 RK3588 与 FPGA 通信实践
  • Windows 部署 OpenAkita 并接入飞书实现本地 AI 助手
  • 渗透测试实战:HackMyVM Hundred 靶场攻防演练

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online