跳到主要内容Python 月相计算与 Web 可视化系统设计 | 极客日志Python大前端算法
Python 月相计算与 Web 可视化系统设计
综述由AI生成项目利用 Python 结合 Matplotlib 与 Web 技术,实现了月相周期的精确计算与动态可视化。核心模块包含朔望月周期算法、月亮形态渲染及 HTML 界面生成。方案解决了中文显示、阴影模拟及交互特效等难点,支持时间轴、曲线图等多种图表展示,适用于天文科普或节日主题开发。
忘忧6 浏览 引言
中秋节承载着团圆与和谐的文化寓意,而月相变化则是这一节日的天文基础。利用 Python 强大的数据处理与绘图能力,我们可以构建一个精确的月相可视化系统,重现天空中月亮的盈亏变化。

项目概述
本系统旨在构建一个功能丰富的月相可视化平台,核心特性包括:
- 精确天文计算:基于 29.53 天朔望月周期的高精度算法
- Web 界面生成:自动生成包含动态图表的 HTML 可视化页面
- 视觉特效丰富:星空背景、流星效果、月亮光晕等 CSS3 动画
- 多维度展示:支持时间轴、曲线图、年度概览等多种图表形式
技术架构解析
环境依赖
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 - 核心计算引擎
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from datetime import datetime, timedelta
import math
import base64
io
warnings
matplotlib rcParams
warnings.filterwarnings()
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(, , )
import
import
from
import
'ignore'
'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 = 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)
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 防止内存泄漏
结语
本方案通过 Python 后端计算与前端可视化相结合,实现了月相数据的精准呈现。代码逻辑清晰,易于扩展,可作为天文科普或节日主题开发的基础模板。
相关免费在线工具
- 加密/解密文本
使用加密算法(如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