跳到主要内容Python 月相可视化项目实战:从计算到 Web 渲染 | 极客日志Python大前端算法
Python 月相可视化项目实战:从计算到 Web 渲染
本项目基于 Python 与 Matplotlib 构建月相可视化系统,涵盖朔望月周期算法、动态阴影绘制及 HTML 界面生成。实现时间轴、曲线图等多种图表形式,并融入中秋文化特效,提供完整的 Web 端展示方案。
芝士奶盖1 浏览 中秋节不仅是团圆的节日,也是观察天象的好时机。我们不妨用 Python 这门优雅的编程语言,来创造一个富有诗意的月相可视化器。本文将带您通过代码的艺术,重现天空中月亮的盈亏变化。
项目概述
我们将构建一个功能丰富的月相可视化系统,主要包含以下特性:
- 精确的天文计算:基于 29.53 天月相周期的高精度算法
- Web 界面生成:自动生成华丽的 HTML 可视化界面
- 视觉特效丰富:星空背景、流星效果、月亮光晕等
- 多维度展示:时间轴、曲线图、年度概览等四种图表
- 中秋文化融入:诗词展示、传统装饰元素
技术架构解析
环境准备
安装依赖:
pip install numpy matplotlib datetime base64 io warnings
项目结构
moon-phase-visualizer/
├── moon_calculator.py
├── generate_html.py
└── moon_phase_2025_mid_autumn.html
实现思路
月相计算核心
月相变化周期是 29.53 天,选定 2025 年 9 月 25 日作为参考新月。通过计算目标日期与参考点的时间差,结合朔望月周期进行数学建模。
算法的关键是将连续时间变化映射到 0-1 的月相值:前半周期(新月→满月),后半周期(满月→新月)。
可视化难点
matplotlib 中文显示问题通过多字体回退解决:微软雅黑 → 黑体 → 其他系统字体。
月亮绘制用圆形 + 椭圆阴影实现,阴影宽度根据月相值动态计算。中秋节特效包括多层光晕和星星装饰。
核心模块设计
moon_calculator.py - 核心计算引擎
这里的核心在于利用 datetime 处理时间差,并通过模运算映射到月相周期。
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()
plt.rcParams[] = [, , , ]
plt.rcParams[] =
:
():
.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(, , )
'Agg'
'font.sans-serif'
'Microsoft YaHei'
'SimHei'
'Arial Unicode MS'
'DejaVu Sans'
'axes.unicode_minus'
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() 确保了计算的精度。
可视化渲染类
在绘制时,我们用 patches.Circle 绘制月亮基础形状,椭圆阴影模拟月相变化。中秋特效则通过多层光晕加五角星装饰来实现。
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 "满月"

四种图表实现详解
时间轴图表 - 连续月相展示
这里的关键点在于间距设置避免重叠,以及大画布确保显示完整。中秋节用红色高亮加边框装饰。
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')
月相曲线图 - 数学规律可视化
视觉设计上,深蓝背景加金色曲线营造夜空效果。fill_between 创建填充区域增强视觉效果,水平垂直参考线标注关键点。
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})')
当前月相图
在技术实现上,8x8 正方形画布保证月亮圆形显示,大尺寸突出视觉效果,圆角文本框显示详细信息。
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))
图像 Base64 编码
要点在于内存缓冲区避免临时文件,dpi 平衡质量与大小,并释放内存防止泄漏。
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')
HTML 界面生成
generate_html.py - 界面组装器
接下来我们来处理界面组装。这里实例化可视化器,生成各个图表并转换为 Base64 嵌入 HTML。
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)
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 交互特效
最后补充前端交互,创建随机分布的闪烁星星,流星从随机位置斜向划过,并自动清理 DOM 防止内存泄漏。
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);
}
结语
代码如明月,永远照亮前行的路。希望这个项目能为你提供一个将天文计算与 Web 可视化结合的参考方案。
相关免费在线工具
- 加密/解密文本
使用加密算法(如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