跳到主要内容Python 月相可视化系统:从天文计算到 Web 界面生成 | 极客日志Python大前端算法
Python 月相可视化系统:从天文计算到 Web 界面生成
综述由AI生成Python 月相可视化项目通过天文算法精确计算朔望月周期,结合 Matplotlib 绘制不同阶段的月亮形态,并利用 HTML/CSS/JS 生成交互式 Web 界面。核心包含月龄计算、阴影模拟、星空特效及图表渲染,实现了从数据模型到视觉呈现的完整流程,适用于节日主题展示或天文科普场景。
ByteFlow6 浏览 项目概述
本项目构建了一个功能丰富的月相可视化系统,旨在通过代码重现天空中月亮的盈亏变化。主要特性包括基于朔望月周期的高精度天文计算、自动生成 HTML 可视化界面以及星空背景等视觉特效。
技术架构解析
核心依赖与结构
首先确保环境配置正确,安装必要的科学计算与绘图库:
pip install numpy matplotlib datetime base64 io warnings
项目采用模块化设计,核心文件如下:
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')
plt.switch_backend('Agg')
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] =
:
():
.lunar_cycle =
.reference_new_moon = datetime(, , )
():
days_since_new_moon = (date - .reference_new_moon).total_seconds() / ( * )
cycle_position = (days_since_new_moon % .lunar_cycle) / .lunar_cycle
cycle_position <= :
phase = cycle_position *
:
phase = - (cycle_position * )
phase
():
days_since_new_moon = (date - .reference_new_moon).total_seconds() / ( * )
days_since_new_moon % .lunar_cycle
():
mid_autumn_2025 = datetime(, , )
((date - mid_autumn_2025).days) ==
():
year == :
datetime(, , )
year == :
datetime(, , )
year == :
datetime(, , )
:
datetime(, , )
False
class
MoonPhaseCalculator
"""月相计算器类"""
def
__init__
self
self
29.530588853
self
2025
9
25
def
get_moon_phase
self, date
"""计算指定日期的月相"""
self
24
3600
self
self
if
0.5
2
else
2
2
return
def
get_moon_age
self, date
"""计算月龄"""
self
24
3600
return
self
def
is_mid_autumn_festival
self, date
"""判断是否为中秋节(2025 年 10 月 6 日)"""
2025
10
6
return
abs
0
def
get_mid_autumn_date
self, year=2025
"""获取指定年份的中秋节日期"""
if
2025
return
2025
10
6
elif
2024
return
2024
9
17
elif
2026
return
2026
9
25
else
return
2025
10
6
lunar_cycle 设定为精确的朔望月天数,保证长期计算的准确性。
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 构建月亮主体,淡黄底色配合金色边框模拟真实质感。
- 阴影宽度公式
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 编码
为了将图表嵌入 HTML,需要将 Matplotlib 图形转换为字符串。
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;
}
JavaScript 交互特效
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 元素,防止内存泄漏。
- 整体交互流畅,无需刷新页面即可体验动态效果。
相关免费在线工具
- 加密/解密文本
使用加密算法(如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