ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

把闲置电视变成月相可视化大屏:前端天文计算与实现指南

把闲置电视变成月相可视化大屏:前端天文计算与实现指南 家里客厅角落一直放着那台退役的32寸电视智能系统卡到开个App要等半天扔了可惜卖又不值钱。直到有天我路过看它一眼突然冒出个想法与其让它继续吃灰不如把它改造成一块“懂月亮的屏”——每天扫一眼就知道今晚月亮什么状态、农历几号、适不适合拍星空。于是在折腾了一个多月之后有了LunaTV这个项目一个基于浏览器运行的月相可视化大屏应用顺便还衍生出了一个同名短视频频道。如果你恰好有一块闲置屏幕又对天文计算、前端可视化或者“用代码自动生成视频素材”这些事感兴趣这篇文章应该能给你不少可以直接抄作业的干货。我会把从项目缘起、算法选型、前端实现到设备适配踩坑的过程原原本本讲一遍包括那些你搜文档绝对搜不到的细节。1. 从一块吃灰屏幕到“家庭天文终端”LunaTV 的项目由来1.1 闲置硬件的自我救赎这块32寸老电视的原始用途是卧室观影后来家里人习惯了用平板它就慢慢被冷落了。电视本身的系统是安卓虽然流畅度一般但内置浏览器还能用——这成了整个项目成立的关键前提。我真正想要的东西其实是一个“看一眼就能get所有信息”的信息面板。市面上的电子日历相框我也研究过但我想要的信息它们给不了今天的月相是什么样的、离下一个满月还有几天、月出月落大概在几点、今晚到底适不适合拍星星。那段时间正好在陪孩子观察月亮每天都要在手机上翻App查当天月相体验很割裂。手机上的月相卡片虽然精致但永远只是个小图标缺少那种“整块屏幕都是今晚星空”的氛围感。1.2 “Tv”不是电视台是客厅的那块大屏项目定名LunaTV其实没有什么高深的含义。Luna是罗马神话中的月亮女神TV就是客厅这台设备。连起来读就是“月亮电视”——一块专门为了看月亮、看日历、看天文信息而存在的大屏。这个定位后来也帮了大忙。因为“TV”两个字我在设计时天然就考虑了遥控器操作、大字体远距离阅读、低亮度夜间模式这些问题。这些在手机上根本不会遇到的约束反而让这个项目变得更有意思。项目从第一天起就定了三个设计原则后面所有技术选型都围着它们转离线优先天文数据必须本地计算不能依赖外部API否则断网就白屏这是底线。大屏优先界面是为2米外扫一眼设计的字号要大信息层级要清楚。自动运行开机自动打开页面24小时挂在墙上不需要任何人工干预。1.3 三条从立项就定好的设计原则这三个原则看着简单执行起来却很有讲究。比如“离线优先”意味着我必须在浏览器里自己实现月相计算能力这直接决定了后面要啃下一整块天文算法的硬骨头。“大屏优先”则让我把绝大多数精力花在了布局和信息密度上。一开始我犯过所有“手机思维”的错——在一个背影屏上放了一堆列表、按钮、设置项结果站在3米外什么都看不清。后来彻底推倒重来主页只剩三个大模块中间的月相圆盘、左边的农历日历、右边的天文信息每个模块字体都放大到能轻松辨认的程度。“自动运行”看似简单其实牵扯到电视浏览器缓存策略、屏幕常亮、前端定时刷新等一系列问题这个放到后面踩坑章节单独讲。2. 月相算法选型自己算还是调接口我最后选了“两条腿走路”2.1 为什么一开始想调API后来放弃了刚开始我自然是奔着“省事”去的找了一圈免费的天文API。但做了一轮技术预研之后发现公共API有三个致命问题限流家庭大屏是7x24小时开机的每小时刷新一次就是一天24次请求某些免费API的限制在每小时几百次看着够用但偶尔有异常重试就很容易触发风控。时区很多API返回的是UTC时间如果直接丢给前端格式化就会出现和本地日历对不上号的诡异情况。往返延迟每次查询要经历一次完整的HTTP进出断网时页面直接没法看这违反了项目最底线的“离线优先”原则。所以我做了一个判断外部API可以作为“辅助数据源”但绝对不能是主数据源。核心的月相计算必须本地完成。2.2 从儒略日到月龄一个能背下来的月相近似公式如果你只是想知道“今天是新月、上弦、满月还是下弦”其实不需要抄起《天文算法》就开始推球面天文学有很多近似公式完全够用。我自己用OpenAI和搜索引擎查了不同方案后最后选择了一条非常经典的简化路线。思路是这样的儒略日JD是天文学里最常用的纪日法它把日期和时刻统一到一个连续递增的数值上计算机处理起来很方便。知道任意时刻的儒略日再减去一个已知新月时刻的儒略日除以朔望月周期就能得到“从某个新月起过去了多少个朔望月”。下面的JavaScript函数是这套计算的核心代码非常简单却足够用来做日常展示// 计算指定日期的月相近似方案误差在可接受范围内 function calcMoonPhase(date) { // 1. 将Date对象转换为儒略日 // Unix时间戳是毫秒除86400000得到天数再加上儒略日与Unix纪元的偏移2440587.5 const jd date.getTime() / 86400000 2440587.5; // 2. 已知的平均新月参考点2000年1月6日18:14 UTC, 对应儒略日2451550.1 const knownNewMoon 2451550.1; // 3. 朔望月平均周期天 const synodicMonth 29.530588853; // 4. 距今已经历的朔望月数可能带小数 const lunations (jd - knownNewMoon) / synodicMonth; // 5. 取小数部分得到当前朔望月内的进度 phase范围0~1 let phase lunations - Math.floor(lunations); if (phase 0) phase 1; // 6. 月龄天进度乘以周期 const age phase * synodicMonth; // 7. 照明比例近似值0~1新月接近0满月接近1 const illumination (1 - Math.cos(2 * Math.PI * phase)) / 2; return { phase, // 0新月, 0.25上弦, 0.5满月, 0.75下弦 age, // 月龄单位天 illumination // 照明比例单位% }; }这个函数输出三个核心数值phase、age、illumination。phase在0和1之间走完一个月相周期0对应新月、0.25上弦、0.5满月、0.75下弦。age就是“今天月亮几岁了”从新月开始算0天到下一次新月约29.53天。illumination是月面被照亮面积的近似比例。这个公式的误差来源主要是朔望月周期取的是平均值且没有修正地球公转轨道偏心率带来的影响。对“今天大概是什么月相”这种展示需求误差通常在几小时内完全够用。但如果要精确到“今晚几点能看到月落”就必须上更严谨的库了。2.3 为什么最终生产环境用了 astronomy-engine简化公式适合学习到生产环境我还是有点不放心。月相倒是无所谓真正需要精确计算的是“月出月落时间”——这对一个天文终端来说是不可或缺的功能。月出月落受月球赤纬、观测地经纬度、大气折射等多种因素影响手写公式工作量很大。我最后引入了开源库astronomy-engine。它支持JavaScript、C、Python等多种语言内置了月相搜索、月出月落、日月角直径、晨昏光等一大堆计算方法精度到分钟级别这正是我需要的。import { Astronomy, Body, Observer } from astronomy-engine; const observer new Observer(30.25, 120.15, 10); // 经纬度与海拔米 // 搜索下一个满月时刻 const nextFull Astronomy.SearchMoonPhase(0.5, new Date(), 45); console.log(nextFull.date); // 计算某一天月出月落 const moonEvents Astronomy.SearchRiseSet(Body.Moon, observer, -0.8, 1, new Date());引入这个库之后我把自己写的近似公式降级成“时间复杂度几乎为零的预览用算法”precision计算统一走astronomy-engine。这种“两条腿走路”的策略既保持了页面的流畅性也保证了关键信息的准确性。顺带提醒一句astronomy-engine用的是GPL v3许可证这意味着如果你要分发二进制或对外提供服务有开源义务。个人玩无所谓商用或开源发布前要留意协议要求。这一点在后面给项目打包发布时很容易踩坑。2.4 农历日期是本地刚需但别自己造轮子LunaTV界面上一行很重要的信息是农历。国内用户习惯用农历来判断节日、生日和对传统节气的感知。如果只用公历项目就少了“中国感”价值会打折扣。一开始我觉得农历也就是个“查表”结果深入了解后发现水很深农历是大阴历加闰月而且“闰哪个月”不是简单循环要结合节气中气的位置完全自己实现的话基本等于要把中国历法研究一遍。我的建议很直接不要自己造轮子直接用lunar-javascript这个开源库。它实现了从公历到农历、干支、生肖、节气、各种传统历法日期的完整转换接口也相对干净const { Solar } require(lunar-javascript); const solar Solar.fromDate(new Date()); const lunar solar.getLunar(); console.log(lunar.toString()); // 二〇二四年三月初九 console.log(lunar.getJieQi()); // 谷雨等节气 console.log(lunar.getShengXiao()); // 生肖唯一要留意的是农历与公历的映射关系需要覆盖闰月、大月小月等边界情况库里有对应测试。后面我专门为2023年闰二月、2025年春节前后做过一批用例保证UI上“农历初一”和公历日期不错位。3. 技术选型为什么这块大屏选择了 Web 而不是原生 TV 应用3.1 横向对比四套候选方案当时我认真考虑过几条技术路线方案开发成本硬件要求可维护性最终选择Android TV 原生应用高需要熟悉TV端焦点框架电视系统版本不能太老一般否树莓派 深海屏幕保护程序低但定制空间小额外一块ARM板子低否智能相框/墨水屏无但信息完全不可定制再添置新硬件无否Web页面 电视浏览器中前端可控性高任意带浏览器的智能设备高是最终选Web方案的理由可以总结成四个字硬件无关。只要这台老电视能打开浏览器不管跑的是安卓、Tizen还是WebOS我的应用都能跑。而且前端技术栈我可以自己完全掌控想改布局改数据都不需要发版走应用商店审核直接把页面更新到静态服务器上就行。3.2 前端可视化SVG画月亮、Canvas画星空各司其职页面里的月相圆盘一开始我试过用Canvas逐帧绘制效果不错但代码复杂。后来换成了SVG方案把月相圆盘做成了一个可无限放缩的矢量组件清晰度无论在1080P还是4K屏幕上都能保持。星空背景则交给Canvas处理因为星星的数量大、位置随机用Canvas批量绘制性能更好。SVG和Canvas的分工边界在项目里非常清晰SVG负责月亮、关键标注、农历文字、数据卡片。这类元素有明确形状需要响应式缩放和交互。Canvas负责动态星空、流星/旋转星轨等氛围粒子。这类元素没有明确的DOM结构量大需要逐帧重绘。3.3 部署形态无服务端静态站点浏览器一开就是成品LunaTV最终被实现成一个纯静态页面没有后端、没有数据库、没有构建链路形态。源码托管在GitHub仓库里使用GitHub Pages做访问入口也可以直接下载到本地用任意静态文件服务器打开。为什么不做服务端一个重要的考虑点是“家庭环境的不确定性”。我的电视通过浏览器访问页面如果页面依赖某个云服务端口那服务挂了我整个屏幕也就挂了。静态站点天然不具备这个问题文件拷到U盘里插电视上看离线页面照样能跑这是最稳的形态。前端技术栈也很克制原生JavaScript 少量第三方库astronomy-engine、lunar-javascript没有引入Vue/React这类框架。对于单页大屏应用原生JS已经够用还能免去打包体积和框架升级的焦虑。如果你也想在本地快速跑起来最省事的方式是起一个简单的HTTP服务在项目根目录运行python3 -m http.server 8080然后电视浏览器打开http://电脑局域网IP:8080就能看到效果了。4. 核心实现手记月相圆盘、时间线滑块与自动视频合成4.1 北半球视角的月相圆盘怎么画月相圆盘的视觉效果是整个项目最关键的“门面”。我第一次画的时候犯了一个典型错误直接用两个半圆叠来代替月相结果月亮灰蒙蒙一片完全不像。正确的画法其实是把“月球亮面”看作一个由两个弧线围成的图形外轮廓是圆的边界内部明暗交界线是椭圆弧。随着月相变化椭圆弧的“胖瘦”和方向会变化上弦月时暗面朝西下弦月时暗面朝东。我最终采用的方式是用SVG的clipPath先创建一个月球圆形遮罩然后在遮罩内部绘制一个位移的白色“亮半球”通过控制亮面的横向位移和缩放系数就能模拟出从新月到满月的渐变。!-- 简化的月相圆盘实现 -- svg viewBox-50 -50 100 100 width300 height300 defs clipPath idmoonCircle circle cx0 cy0 r45 fillwhite/ /clipPath /defs !-- 月球暗面 -- circle cx0 cy0 r45 fill#2a2a3a/ !-- 月球亮面通过clipPath裁减到圆形内 -- g clip-pathurl(#moonCircle) !-- 亮面主体宽度随月相变化 -- ellipse cx${phaseOffset} cy0 rx${illumination * 90} ry90 fill#f5f0e1 / /g /svg这个方案的核心是动态计算phaseOffset和illumination。当照明比例为0.5时亮面是一整个半圆为0.05时只剩一弯月牙接近1时整个圆盘变白视觉效果非常自然。如果你自己写不建议一上来就用我这段代码跑而是先画一个静态半月亮把坐标系和几何关系搞清楚再去调整参数。比如椭圆亮面的rx和亮面圆心的横向偏移之间存在一个光滑的数学关系直接用一个参数驱动月相动画才会顺滑。4.2 全局时间线与“未来30天月相预览”单看今天当然不够我设计了一个轻量级时间线滑块可以拖动预览从今天到未来30天的月相、农历和月出月落信息。这对“计划拍月亮”非常有用哪天是满月、哪天适合观测一眼就能规划。实现并不复杂页面维护一个currentTime变量初始值是当前时间。滑块每次产生input事件就把这个变量更新为今天 滑动天数然后触发一次所有数据卡片和月相圆盘的重新计算。timeline.addEventListener(input, (e) { const dayOffset Number(e.target.value); currentTime.setDate(currentTime.getDate() dayOffset); renderAll(currentTime); });真正要注意的是性能。astronomy-engine的月出月落计算要比纯数学公式慢一个数量级滑块拖动时如果每一下都去全量计算所有天文目标参数页面会明显卡顿。我的优化办法是给计算逻辑加了个“节流阀”用户拖动过程中只更新SVG月相圆盘和农历文字这些轻量数据等松手之后再重新计算月出月落、亮面照明比例等较重数据。这样既保证了交互流畅度也不会牺牲最终展示的准确性。4.3 月出月落与天文晨昏光信息密度怎么取舍原始需求里“今晚适合不适合拍星星”其实包含很多维度月出月落时间、月光干扰程度、晨昏光阶段、天气状况。一口气全塞到屏幕上信息密度就太高了。我最后的大屏布局做了减法主区域大号数字显示月龄与照明比例旁边配月相圆盘。左侧卡片农历日期、距离下一个新月/满月的倒计时。右侧卡片今天月出/月落时间以及一个用百分比表示的“月光干扰指数”。底部滚动条未来几天的月相缩略图方便快速预览趋势。月光干扰指数是我自己定义的一个指标如果月亮已经落下指数为0如果月亮正在天上则结合月相照明比例粗略算一个0到100的值。满月且在天上时指数最高。这个指标虽然粗暴但对“今晚适不适合拍星空”的判断很有参考价值。4.4 自动合成“月相晚间报告”从Canvas到MediaRecorder这个功能是LunaTV真正“出圈”的地方。我把项目做成了“内容生成器”每天自动把月相变化渲染成一段几秒钟的短视频输出格式适配各大短视频平台名叫“月相晚间报告”。这直接促成了同名短视频频道的诞生。浏览器端生成视频的方案并不复杂用Canvas不停地画出当天月相圆盘的若干帧通过canvas.captureStream()获取视频流再用MediaRecorder录制为WebM格式。核心代码如下const canvas document.getElementById(animCanvas); const ctx canvas.getContext(2d); const stream canvas.captureStream(30); // 30帧/秒 const recorder new MediaRecorder(stream, { mimeType: video/webm }); const chunks []; recorder.ondataavailable (e) chunks.push(e.data); recorder.onstop () { const blob new Blob(chunks, { type: video/webm }); // 下载或上传blob }; // 开始录制前先预渲染60帧动画 recorder.start(); for (let f 0; f 60; f) { drawFrame(f); // 绘制第f帧月相 await sleep(1000 / 30); // 按30fps的节奏输出 } recorder.stop();这段代码本身不复杂坑在于MediaRecorder对帧间隔非常敏感。如果你用sleep做定时浏览器一后台就会把定时器节流导致帧率波动、视频音画不同步。我最终是用requestAnimationFrame配合帧序号来实现匀速输出再手动控制总帧数问题才解决。WebM格式在手机端相册能直接看但部分平台对WebM上传支持并不好。我的处理方法是先把WebM作为用户投稿素材需要上传时再用剪辑软件顺手转成MP4或者直接在页面里提供“导出MP4”按钮调用转码库。这块后续有精力可以优化成自动转MP4但作为第一步WebM足够支撑日常内容更新节奏了。5. 踩坑实录时区、浮点误差、农历区间和大屏焦点的四场硬仗5.1 时区错位北京4月5日凌晨的新月到底算不算4月5日这是我在开发中遇到的最隐蔽的一个bug也是所有天文计算新手最容易踩的坑。现象是这样的某个4月5日的早上我看LunaTV屏幕上显示“新月”但我搜天文台公布的日历新月却在4月4日。差了一天页面和官方来源对不上。追查后发现问题出在对“星期几”的理解上。天文算法的输出通常都是UTC时间。比如那次新月瞬间发生在UTC的4月4日18:14而北京是UTC8换算成本地时间已经是4月5日凌晨2点。浏览器里用本地时区格式化日期时会直接显示成“4月5日”。如果用“UTC日期”做展示4月4日的说法会和国内天象日历对不上如果用“本地日期”做展示4月5日又显得和“新月瞬间在UTC4日”矛盾。这个问题没有标准答案我的处理原则是计算链路全部使用UTC时间戳只有到展示层才用本地时区格式化。同时在做“今日月相”判断时明确按本地日期重新计算一个“以本地零点为界”的结果并把两个日期合并展示成“本日对应农历x月x日”的形式避免歧义。5.2 浮点与儒略日的边界不需要处理闰秒但要注意日界线在天文计算里时间戳精度、儒略日小数位、浮点精度是三个容易混淆的问题。先说结论LunaTV只处理1970年到2100年之间的日期完全没有必要去关心闰秒问题。闰秒对月相计算的影响在秒量级而我的展示精度是“天”差了半分钟根本看不出来。真正要注意的是JavaScript的Date在比较早的历史日期上存在兼容性问题例如1900年以前的日期解析在不同浏览器里行为不一致。项目设定范围是当前日期前后50年所以完全绕开了这个坑但如果你打算扩展成“万年历”应用一定要自己把日期转换到整数时间戳避免依赖浏览器解析。浮点方面astronomy-engine内部使用高精度浮点计算但在我们封装的calcMoonPhase函数里如果直接用date.getTime()得出的毫秒数除以86400000在极端情况下会产生几千分之一天的误差。这个误差在展示层可以忽略但当你拿它去比较“是否跨过新月点”时有可能偶尔错判三四十分钟。稳妥做法是保留足够多的有效位数或者在比较边缘情况时改用astronomy-engine的SearchMoonPhase结果。5.3 农历“初一”和公历日期的映射测试要覆盖春节农历转公历看似简单但边界条件多到令人头秃。我最早没有做边界测试结果在2023年闰二月的时候界面显示的农历和日历App差了一个月。原因是这样的2023年有闰二月农历年里有13个月而我当时用的一个“暴力查表”逻辑把闰月丢了。后来果断换成lunar-javascript它自带闰月处理。但即使换了库我仍然建议你手动加几个硬测试2023年3月22日是公历闰二月初一2025年1月29日是公历正月初一春节2033年11月存在一个非常罕见的历法问题日历库处理也不一样如果不做展示用途可以提前跳过。测试方式很简单我写了一个测试脚本输入一组“公历日期”断言库输出的“农历”与我手动从日历App上摘录的值一致。跑过一遍心里就踏实了。5.4 电视浏览器适配焦点、分辨率、GPU性能三连坑把页面从电脑搬到电视上很快就遇到了几个电视浏览器特有的问题。第一坑遥控器焦点没有默认行为。普通网页在电视浏览器里打开用遥控器方向键时焦点不会像原生TV应用那样到处移动。你必须监听keydown事件手动控制高亮元素。我用一个简单的focusIndex变量把主页面的左右卡片做成可聚焦组件方向键左右切换确定键跳转详细页。第二坑分辨率与CSS像素不一致。很多4K电视的浏览器中CSS像素并不等于物理像素。页面按显示器尺寸写得美美的到了电视上字小得看不清。解决方式是给整个App包一个根容器用min()和vw等单位动态放大字号并用window.devicePixelRatio做辅助判断。第三坑GPU性能比想象中弱。电视SoC的GPU性能远不如手机。页面里如果放大量模糊滤镜、阴影特效帧率会直线下降。我一开始给月亮做了很漂亮的泛光效果结果在老电视上卡成PPT。最后把所有filter: blur()全部移除改成半透明渐变的伪泛光观感差异不大但帧率从个位数回到了50帧以上。第四坑浏览器缓存。电视浏览器对网页缓存的处理不像电脑那么规范更新后有概率继续读旧资源。解决方式是在引入JS和CSS的URL后面加上版本号参数每次发版手动改一下版本号强制电视拉到最新内容。这些坑单独看都是小事但叠加起来会让一个“应该很简单”的大屏项目变得极其烦躁。我强烈建议只要条件允许把开发调试的目标设备从第一天就接上不要等页面写完了再拿到电视上验证。6. 当项目变成内容源LunaTV 同名频道的意外成长6.1 每天自动渲染一帧“今日月相”视频素材LunaTV上线跑了两周之后我突然意识到它已经天然是一个内容生产线每天晚上都会生成当天的月相数据、月出月落、农历信息以及一段月相圆盘的动画。于是我在后端定时任务里加了一段逻辑每天UTC 12点北京时间20点用无头浏览器打开LunaTV页面截一张全屏高清图同时用Canvas生成一段3秒的月相动画然后保存到本地素材库。第二天早上我只需要从素材库里挑一张图配上一句“今晚月亮状态”就能发一条短视频。这大大降低了内容的创作成本。原来做一条天文科普视频光找素材可能就要半小时现在素材全部自动化生成我只要负责文案和剪辑节奏。6.2 一个人做频道的节奏让代码帮你维持日更一个人做内容最怕的就是“断更焦虑”。LunaTV的自动化素材生成机制让我即便哪天下班晚了也能在5分钟内完成一条内容。具体流程是这样的晚上自动截完图之后我手机上会收到一条推送附带上当天的月相数据、月出月落时间和一段自动生成的文案草稿。我只需要人工校对一下文案里有没有常识错误再顺手改一两个措辞就能发布。当然自动化只解决了“素材”问题没有解决“选题”问题。频道内容如果每天都是同一张月相图观众很快就会审美疲劳。我的应对方式是用一份“选题库”辅助把满月、新月、上弦月、下弦月这些关键天象节点提前标注出来每逢特殊天象就做一期稍微深入的拆解视频日常就发轻量“月相日记”。这样既有稳定的日更频率又能在大节点上爆发出内容亮点。6.3 后续想做的方向与给后来者的建议这个项目从一块吃灰屏幕开始最后变成了一个集大屏信息展示、内容自动生成、天文知识科普于一体的自驱动项目。我已经把它开源在GitHub上后续计划做三个方向的扩展多城市支持目前月出月落计算固定在杭州后面会让用户切换城市。天气与光污染图层接入天气数据源在月相卡片旁边加一个今晚“观测指数”直接回答“能不能拍星星”。更多模板输出月相晚间报告不止一种模板可以生成拟物风、极简风、复古天文画册风等多种视觉素材。如果你也想做一个类似的“家庭天文终端”我给你三条最实在的建议第一不要一上来就啃天文算法。先用开源库把核心功能跑通再去读Meeus的《Astronomical Algorithms》也不迟。算法是手段不是目的。第二优先做好时区和农历这两个本地化细节。它们直接影响家里人每天愿不愿意抬头看这块屏幕。技术再炫如果农历错了在我家就会被打入冷宫。第三电视端适配永远比你想的麻烦。把浏览器缓存、焦点控制、设备像素比、GPU性能这些非功能需求尽早纳入设计不要等页面写完了再回头补。我至今还记得第一次在家里那台老电视上看到满月高悬、月龄、农历日期同时显示时的感受——一块吃灰已久的屏突然有了“被需要”的价值。后来孩子每天晚上都会跑过来看一眼月相然后兴奋地告诉我“明天月亮更圆了”。这个瞬间让我确定LunaTV不是我一时兴起的玩具而是一块真正连接着家庭生活的小小天文终端。如果你家里也有一块闲置屏幕不一定要完整复刻我这一套方案。哪怕只是在上面跑一个简单的月相日历把“看一眼月亮”重新带进日常生活就已经值回票价了。
返回列表