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

項(xiàng)目概述
我們將構(gòu)建一個功能豐富的月相可視化系統(tǒng),主要包含以下特性:
- 精確的天文計(jì)算:基于29.53天月相周期的高精度算法
- Web界面生成:自動生成華麗的HTML可視化界面
- 視覺特效豐富:星空背景、流星效果、月亮光暈等
- 多維度展示:時間軸、曲線圖、年度概覽等四種圖表
- 中秋文化融入:詩詞展示、傳統(tǒng)裝飾元素
技術(shù)架構(gòu)解析
# 安裝依賴 pip install numpy matplotlib datetime base64 io warnings
項(xiàng)目結(jié)構(gòu)
moon-phase-visualizer/
├── moon_calculator.py # 核心計(jì)算引擎
├── generate_html.py # HTML生成器
└── moon_phase_2025_mid_autumn.html # 生成的界面
實(shí)現(xiàn)思路
月相計(jì)算核心
月相變化周期是29.53天,選定2025年9月25日作為參考新月。通過計(jì)算目標(biāo)日期與參考點(diǎn)的時間差,結(jié)合朔望月周期進(jìn)行數(shù)學(xué)建模。
算法的關(guān)鍵是將連續(xù)時間變化映射到0-1的月相值:前半周期(新月→滿月),后半周期(滿月→新月)。
可視化難點(diǎn)
matplotlib中文顯示問題通過多字體回退解決:微軟雅黑 → 黑體 → 其他系統(tǒng)字體。月亮繪制用圓形+橢圓陰影實(shí)現(xiàn),陰影寬度根據(jù)月相值動態(tài)計(jì)算。中秋節(jié)特效包括多層光暈和星星裝飾。
核心模塊設(shè)計(jì)
moon_calculator.py- 核心計(jì)算引擎
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')
# 設(shè)置matplotlib為非交互式后端
plt.switch_backend('Agg')
# 優(yōu)化中文字體設(shè)置
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
class MoonPhaseCalculator:
"""月相計(jì)算器類"""
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):
"""計(jì)算指定日期的月相"""
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):
"""計(jì)算月齡"""
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()通過時間差和模運(yùn)算計(jì)算月相值- 使用
total_seconds()確保計(jì)算精度
可視化渲染類
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
# 繪制月亮基礎(chǔ)圓形
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 "滿月"
繪制要點(diǎn):
- 用
patches.Circle繪制月亮基礎(chǔ)形狀 - 橢圓陰影模擬月相變化:
shadow_width = size * 2 * (1 - phase) - 中秋特效:多層光暈 + 五角星裝飾
- 顏色搭配:淡黃底色 + 金色邊框

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

月相曲線圖 - 數(shù)學(xué)規(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')
# 重要標(biāo)記線
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})')
設(shè)計(jì)要點(diǎn):
- 深藍(lán)背景+ 金色曲線營造夜空效果
fill_between創(chuàng)建填充區(qū)域增強(qiáng)視覺效果- 水平/垂直參考線標(biāo)注關(guān)鍵點(diǎn)
- 紅色星標(biāo)突出中秋節(jié)位置

當(dāng)前月相圖
def create_current_moon(self):
"""創(chuàng)建當(dā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))
技術(shù)特點(diǎn):
- 8x8正方形畫布保證月亮圓形顯示
size=3.0大尺寸突出視覺效果- 圓角文本框顯示詳細(xì)信息

圖像Base64編碼
def fig_to_base64(self, fig):
"""將matplotlib圖形轉(zhuǎn)換為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')
要點(diǎn):
- 內(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特效設(shè)計(jì)
/* 星空背景動畫 */
@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);
}特效實(shí)現(xiàn):
- 120顆隨機(jī)分布的閃爍星星
- 流星從隨機(jī)位置斜向劃過
- 自動清理DOM防止內(nèi)存泄漏
結(jié)語
正如古人所言:"但愿人長久,千里共嬋娟。"愿我們的代碼也能像這輪明月一樣,在技術(shù)的夜空中永遠(yuǎn)閃耀著智慧的光芒,連接著傳統(tǒng)與未來,架起文化與科技的橋梁。
以上就是基于Python實(shí)現(xiàn)一個簡單的月相可視化器的詳細(xì)內(nèi)容,更多關(guān)于Python月相可視化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python實(shí)現(xiàn)批量導(dǎo)入1000條xlsx數(shù)據(jù)
本文主要介紹了Python實(shí)現(xiàn)批量導(dǎo)入1000條xlsx數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
基于Python實(shí)現(xiàn)對Excel工作表中的數(shù)據(jù)進(jìn)行排序
在Excel中,排序是整理數(shù)據(jù)的一種重要方式,它可以讓你更好地理解數(shù)據(jù),本文將介紹如何使用第三方庫Spire.XLS?for?Python通過Python來對Excel中的數(shù)據(jù)進(jìn)行排序,需要的可以參考下2024-03-03
Python基于回溯法子集樹模板解決0-1背包問題實(shí)例
這篇文章主要介紹了Python基于回溯法子集樹模板解決0-1背包問題,簡單描述了0-1背包問題并結(jié)合具體實(shí)例形式分析了Python使用回溯法子集樹模板解決0-背包問題的具體實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-09-09
python中把元組轉(zhuǎn)換為namedtuple方法
在本篇文章里小編給大家整理的是一篇關(guān)于python中把元組轉(zhuǎn)換為namedtuple方法,有興趣的朋友們可以參考下。2020-12-12
簡單介紹Python下自己編寫web框架的一些要點(diǎn)
這篇文章主要介紹了簡單介紹Python下自己編寫web框架的一些要點(diǎn),示例代碼基于Python2.x版本,需要的朋友可以參考下2015-04-04
Python+selenium 自動化快手短視頻發(fā)布的實(shí)現(xiàn)過程
這篇文章主要介紹了Python+selenium 自動化快手短視頻發(fā)布,通過調(diào)用已啟用的瀏覽器,可以實(shí)現(xiàn)直接跳過每次的登錄過程,上傳功能的使用方法通過代碼給大家介紹的也非常詳細(xì),需要的朋友可以參考下2021-10-10
使用python實(shí)現(xiàn)baidu hi自動登錄的代碼
使用python自動登錄baidu hi的代碼,有需要的朋友可以參考下2013-02-02

