基于Python實現(xiàn)一個簡單的月相可視化器
引言
中秋節(jié),這個承載著千年文化的傳統(tǒng)節(jié)日,以其獨特的滿月寓意著團圓與和諧。我們不妨用Python這門優(yōu)雅的編程語言,來創(chuàng)造一個富有詩意的中秋節(jié)月相可視化器。本文將帶您通過代碼的藝術,重現(xiàn)天空中月亮的盈虧變化,并在中秋節(jié)這個特殊的日子里,為我們的程序增添一抹傳統(tǒng)文化的色彩。

項目概述
我們將構建一個功能豐富的月相可視化系統(tǒng),主要包含以下特性:
- 精確的天文計算:基于29.53天月相周期的高精度算法
- Web界面生成:自動生成華麗的HTML可視化界面
- 視覺特效豐富:星空背景、流星效果、月亮光暈等
- 多維度展示:時間軸、曲線圖、年度概覽等四種圖表
- 中秋文化融入:詩詞展示、傳統(tǒng)裝飾元素
技術架構解析
# 安裝依賴 pip install numpy matplotlib datetime base64 io warnings
項目結構
moon-phase-visualizer/
├── moon_calculator.py # 核心計算引擎
├── generate_html.py # HTML生成器
└── moon_phase_2025_mid_autumn.html # 生成的界面
實現(xiàn)思路
月相計算核心
月相變化周期是29.53天,選定2025年9月25日作為參考新月。通過計算目標日期與參考點的時間差,結合朔望月周期進行數(shù)學建模。
算法的關鍵是將連續(xù)時間變化映射到0-1的月相值:前半周期(新月→滿月),后半周期(滿月→新月)。
可視化難點
matplotlib中文顯示問題通過多字體回退解決:微軟雅黑 → 黑體 → 其他系統(tǒng)字體。月亮繪制用圓形+橢圓陰影實現(xiàn),陰影寬度根據(jù)月相值動態(tài)計算。中秋節(jié)特效包括多層光暈和星星裝飾。
核心模塊設計
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')
# 優(yōu)化中文字體設置
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):
"""判斷是否為中秋節(jié)(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):
"""獲取指定年份的中秋節(jié)日期"""
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:朔望月精確天數(shù)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)
# 根據(jù)月相繪制陰影部分
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)
# 中秋節(jié)特效
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) - 中秋特效:多層光暈 + 五角星裝飾
- 顏色搭配:淡黃底色 + 金色邊框

四種圖表實現(xiàn)詳解
時間軸圖表 - 連續(xù)月相展示
def create_timeline_chart(self, center_date=None, days=30):
"""創(chuàng)建時間軸圖表(修復重疊問題)"""
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')
# 中秋節(jié)特殊標記
if is_mid_autumn:
ax.text(x_pos, 3.5, '2025年中秋節(jié)',
ha='center', va='bottom',
fontsize=14, fontweight='bold',
color='red')
實現(xiàn)關鍵:
x_pos = i * 3.5:間距設置避免重疊- 24x12大畫布確保顯示完整
- 中秋節(jié)用紅色高亮 + 邊框裝飾

月相曲線圖 - 數(shù)學規(guī)律可視化
def create_phase_chart(self):
"""創(chuàng)建月相圖表"""
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創(chuàng)建填充區(qū)域增強視覺效果- 水平/垂直參考線標注關鍵點
- 紅色星標突出中秋節(jié)位置

當前月相圖
def create_current_moon(self):
"""創(chuàng)建當前月相圖"""
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')
要點:
- 內(nèi)存緩沖區(qū)避免臨時文件
dpi=150平衡質(zhì)量與大小plt.close(fig)釋放內(nèi)存防止泄漏
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交互特效
// 創(chuàng)建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);
}特效實現(xiàn):
- 120顆隨機分布的閃爍星星
- 流星從隨機位置斜向劃過
- 自動清理DOM防止內(nèi)存泄漏
結語
正如古人所言:"但愿人長久,千里共嬋娟。"愿我們的代碼也能像這輪明月一樣,在技術的夜空中永遠閃耀著智慧的光芒,連接著傳統(tǒng)與未來,架起文化與科技的橋梁。
以上就是基于Python實現(xiàn)一個簡單的月相可視化器的詳細內(nèi)容,更多關于Python月相可視化的資料請關注腳本之家其它相關文章!
相關文章
Python實現(xiàn)批量導入1000條xlsx數(shù)據(jù)
本文主要介紹了Python實現(xiàn)批量導入1000條xlsx數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-02-02
基于Python實現(xiàn)對Excel工作表中的數(shù)據(jù)進行排序
在Excel中,排序是整理數(shù)據(jù)的一種重要方式,它可以讓你更好地理解數(shù)據(jù),本文將介紹如何使用第三方庫Spire.XLS?for?Python通過Python來對Excel中的數(shù)據(jù)進行排序,需要的可以參考下2024-03-03
Python+selenium 自動化快手短視頻發(fā)布的實現(xiàn)過程
這篇文章主要介紹了Python+selenium 自動化快手短視頻發(fā)布,通過調(diào)用已啟用的瀏覽器,可以實現(xiàn)直接跳過每次的登錄過程,上傳功能的使用方法通過代碼給大家介紹的也非常詳細,需要的朋友可以參考下2021-10-10
使用python實現(xiàn)baidu hi自動登錄的代碼
使用python自動登錄baidu hi的代碼,有需要的朋友可以參考下2013-02-02

