中北大学 2026 年 8 月我们把 194 张实拍照片逐张归并确认了 76 只在册猫咪然后把它们做成了一片可以漫游、可以抽签、可以导出寻猫海报的 Canvas 星空。本文是这个项目的技术复盘数据怎么普查把关、纯前端单文件怎么做到零依赖又流畅、星位怎么从拍脑袋进化成手绘边界多边形确定性撒点以及三个仓库如何用字节级 golden 测试链和 CI 门禁互相咬合。项目已在 GitHub 开源入口见文末。一、这是个什么项目一句话每只猫是一颗星整个校园是一片星域。星色 毛色。橘猫是暖橘星狸花是青灰星三花自带渐变奶牛是蓝白星。星等 人气。一只猫被拍到的次数越多星等越高星周环绕的光点也越多。星位 出没区域。12 大出没区升格为 12 座星座点开宿名能听到每座星官的故事。档案卡 军衔 工位 小传。中北是人民兵工第一校猫学员自然要列队报到喵校长墩墩、橘少校、设备安全官玄铁全黑很难拍……每只猫都有昵称、军衔和工位。它是一个纯前端单文件项目照片和数据全部内嵌双击 HTML 就能打开断网、机房、军训拉练都能跑不产生任何外部请求。同时它有完整的 Web 版托管在 GitHub Pages 上手机浏览器打开一样玩。九月开学季这 76 颗星正好等一届新生来认领——导出一张寻猫海报把校园走一圈猫找到了路也就熟了。这是这个项目最想做的事让认识校园这件事有一个最低压力的入口。二、先讲数据194 张照片怎么变成 76 只猫可视化项目最怕的不是技术翻车而是数据翻车——猫图鉴里出现两只同一只猫这个项目就死了。所以我们在数据上卡得非常严实地普查2026 年 8 月宿舍楼、教学楼、绿化带、食堂后门见到就拍。一部分自己蹲点一部分是学长学姐投喂的照片前后攒了194 张。逐张归并194 张照片逐张比对疑似重复、身份存疑的一律不收录。071 号因为夜拍画质不够直接弃用、编号空着076 和 077 因为同框判读矛盾双双出局——宁可少一只不放一只错的。每一条取舍都写进《归并决策摘要.md》欢迎抽查。证据链每只猫必须有照片证据链可溯源置信度低的直接不收不收录含人脸的照片不虚构行为故事——档案卡里的 50 字小传只写名册里能看到的内容军衔、职务、区域、特征、照片数拼装这是数据红线。隐私全部为校园实地拍摄没有 AI 生成的猫像逐张核验不含人脸进度数据只存浏览器 localStorage可随时清除。这套流程后来被抽象成了12 列名册 CSV 契约编号、昵称、军衔、工位、毛色、特征描述、代表照片文件、照片数量、出没区域、关联照片编号、置信度、备注成为整个模板和工厂的数据入口。编码自动探测utf-8-sig / gb18030 / big5……空编号、重复编号、照片缺失这类新手坑在构建期就带行号报错而不是上线后图片 404。三、星图怎么玩打开之后2550 颗粒子先聚成中北喵星图五个字三色极光垂帘在标题后面然后镜头从猫群深处拉出来星星按远近依次点亮。之后的事情就随意的分层功能看星拖拽/滚轮/双指漫游缩放点星开档案卡按喵名、军衔、岗位、特征搜索无结果给智能建议毛色 chips 一键筛选星图志统计看板实证搭档猫之间有流动连线的星域网络玩起来喵星导览镜头自动飞过六只旗舰猫今日喵签同一天全网抽到同一只喵图鉴遇见打卡10/30/50 只放流星雨十二星宿星轨长曝光星尘变身星星炸成星尘再聚合成猫照片本命猫测试每只猫的数据谱成的专属主题曲Web Audio 实时合成带出门实地寻猫海报12 出没区 × 识别特征竖版 PNG720×1000 分享卡表情包工坊喵星护照 进度码备份恢复导览录屏成 WebM 纪录片彩蛋桌面端鼠标是彗星天色按时辰渐变猫聚簇处漂起对应毛色的星云秋季金落叶、冬季初雪限定点开任何一颗星星尘先炸开再缓缓聚合成这只猫的真实照片对新生来说最好用的是实地寻猫每个出没区列出常驻猫的识别特征一键导出海报存进手机。为了找墩墩你会走到宿舍楼玻璃门前为了找玄铁全黑、夜行、极难拍你会认识设备区——一圈走下来校园地形和楼群方位全摸熟了。四、核心技术实现零依赖单文件凭什么能流畅整个产物是一个 HTML外加若干 base64 照片分片没有框架、没有构建产物、没有任何 CDN 依赖Canvas 2D 手写渲染器。几个关键技术决策1. 发光用 sprite 缓存不用 shadowBlur。shadowBlur在星图这种满天都是发光体的场景是帧率杀手。每档星等/星色的光晕预渲染成离屏 sprite绘制时drawImage贴图粒子数上去也能稳帧。2. dt 步进 120Hz 兼容 DPR 上限。动画全部按dt步进而不是按帧数高刷屏不会加速devicePixelRatio 上限压在 24K 屏不会白白多画四倍像素。3. 帧率自适应不悄悄降级。每秒实测帧率低于阈值自动切省电档星系与星尘抽稀并弹提示告诉用户手机默认均衡档起步、帧率富余自动升回完整档位可手动换且会被记住。4. 照片分片懒加载。75 张照片 base64 内嵌后切成 ≤1.5MB 的photo-data-NN.js分片首屏只取底图关键片约为全量 eager 的 2%点开档案卡才按需取片解码。两种模式懒加载 / eager 整包双击file://都能离线玩。5. 声音全部代码合成。星语音效、每只猫的主题曲毛色定调式、名字定动机Web Audio 振荡器 滤波 延迟实时合成零音频文件遵守浏览器自动播放策略默认关闭。6. 兼容性兜底。roundRectpolyfill 兜 Safari 旧版照片加载失败自动切星夜占位图EXIF 旋转矫正手机竖拍照片方向不对是普查阶段踩过的真实坑浏览器基线 Chrome/Edge 80、Safari 13.1。7. 星轨长曝光的分层。星系画在独立离屏层轨迹缓冲只累积星系层lighter叠加 黑色透明衰减所以长曝光拉出弧形光轨时地图和 UI 不会被糊成一片。五、星位是怎么定的从拍脑袋到手绘边界多边形这是这个项目迭代最多的地方前后经历了三个阶段人工校准v2.8.7 及以前54 颗星手动拖到真实位置坐标写死在 HTML 的 CALIB 表里。准但不可维护——每加一只猫都要手动定位。矩形包络撒点v2.9.0星位改为按编号确定性撒在建成区矩形STAR_BOX内。可复现、可维护但矩形里包含了一些猫不会去的地方。手绘边界多边形v2.9.1当前直接在底图上手绘校区边界生成 51 顶点多边形存进build_meta.star_zone撒点算法换成多边形内的确定性拒绝采样——矩形没变、编号没变但每一颗星都保证落在猫真的会出没的校园轮廓里。上线后实测76/76 全部落形内、0 error。底图也换了官方校园平面图经过逐通道颜色拟合转成了 1920×1121 的手绘风夜空底图二龙山、太行山、吕梁山都在生成脚本tools/make_hand_night_map.py入库可复现。为什么坚持确定性撒点而不是随机数因为同一名册在任何机器上构建两次产物的字节必须完全一致——这是后面整套质量门禁的地基。同时页面上仍保留校准位置模式拖星导出的 JSON 回传后可以逐颗归位到实测坐标。六、三个仓库与渲染真相单源一个课设项目一个仓库就够了为什么会有三个因为定位不同catgalaxy-factory工厂 渲染上游app/starmap_render.py 是唯一真相 meow-starmap开源模板 门面模板 数据层 工具链vendor 渲染器快照 nuc-cat-starmap中北正式版 实例产物由工厂渲染器从数据包直出逐字节一致工厂FastAPI上传名册 CSV 照片 → 14 条校验规则 → 毛色/星等/星位推导 → 注入模板 → 在线预览 → 一键打包离线 HTML。带归并工作台感知哈希给疑似重复预排序、底图标定、主题定制。75 张 266.7MB 原图上传压缩实测 6.35s多进程并行内嵌版生成 0.88s、内存峰值 11.4MB改前是 87.21MB。开源模板 meow-starmap一份名册 CSV 一个照片文件夹 → 一张全离线单文件星图。工厂的渲染器以vendor 快照方式进入模板仓tests/test_vendor_snapshot.py用 sha256 钉死——快照和上游差一个字节 CI 就红杜绝两边各改各的。中北正式版不再手改 HTML产物由export_v29_product.py从数据包直出和模板仓托管的在线版逐字节一致。跨三仓发版顺序是铁律工厂 → 模板仓同步快照 → 正式版直出顺序错了对不上字节。七、质量门禁怎么保证 3320 行模板八代迭代不翻车这套东西最花时间也最值。当前模板仓149 项 Python 测试 全仓 HTML/分片语法门禁工厂仓749 项测试GitHub Actions ubuntu windows 双矩阵Windows 不是凑数杀软占文件的 WinError 5/32/33、路径分隔符、GBK 控制台这些坑只在 Windows 上出现而交付对象恰恰就是 Windows 用户。几条有意思的机制字节级 golden 往返模板由正式版机器提取把原始数据灌回去必须和正式版逐字节相同。v27→v35 八代各有一份 golden 基线入库任何一代的规则改动破坏了历史往返当场变红。托管产物零漂移CI 现场构建schools/并与 GitHub Pages 上的产物逐字节比对——改了模板忘了重建这种事在仓库内就被拦住。版本串注入不变式产物里的版本号由数据包build_meta.json注入模板的__VERSION__token改版本不动模板、不动 golden。这条是被咬过两次之后v2.8.4、v2.8.6 两次发版漏同步版本号固化成测试的。物料打包四重自证对外发布包打出来后自动断言打包物 仓库产物 在线托管三方 sha 一致、分片逐片一致、清单不多不少、README/SKILL 逐份一致。这条闸门上岗当天就抓住了真实事故对外包里的 README 是三个月前的旧版缺了整节许可声明——文档层之前靠人手工同步没人发现。页面一致性另设仪器仓库内全绿 ≠ 线上就新。实测碰到过 push 后 CI 绿、但 GitHub legacy Pages 的自动构建没触发30 次 push 漏 1 次API 无失败记录线上停在旧版本。所以另有一条只读核对直接拉线上 Pages 的字节和本地产物对 sha退出码非 0 就是漂移。一句话总结这套体系的价值它把忘了同步、“两边各改各的”、“线上其实没更新这三类最阴的事故从人肉发现变成了CI 变红”。八、把它复刻到你们学校15 分钟模板仓把门槛压到了零前端基础gitclone https://github.com/TianWenzzzh/meow-starmap.gitcdmeow-starmap# 用自带示例校2 只虚构猫构建uv run--withpillow tools/build.py--pkgschools/示例校--outdist/示例校# 双击 dist/示例校/示例校喵星图.html ✦换成你们学校复制schools/示例校/→ 填 12 列名册、放照片、换底图 → 同一条命令。普查怎么拍、归并怎么做docs/普查与拍摄规范.md和工厂的《跨校落地手册》里有完整 SOP。自己构建的产物可以免费部署到 GitHub Pages想把自己的学校加进模板的多校列表按docs/PR指南.md提交数据包就行。协议是双层的代码与模板MIT——随便改、随便用保留版权声明即可包括商业使用数据与照片CC BY-NC-SA 4.0——署名、非商业、相同方式共享。普查数据和照片是这个项目最不可再生的部分素材来源网络侵权删不能免责。九、踩坑实录都是真金白银的时间Path.write_text在 Windows 上会把整份 LF 文件翻成 CRLF。模板代际脚本一次运行产生 3376 行伪 diff还破坏了模板仓侧 LF的字节基线。现在所有代际脚本一律read_bytes/write_bytes定换行--check把 CRLF 直接判漂移。Windows 杀软会占住 pytest 临时目录好几十秒不放手WinError 5/32/33CI 上文件竞争超时。解法写侧重试预算 CI 排除临时目录扫描。GitHub legacy Pages 自动构建会漏触发且 API 里没有任何失败记录我们实测 30 次 push 漏了 1 次。仓库内一致性测试看不见这一层只能加拉线上字节对 sha的外部核对。发版后别只看 CI 绿。版本号字面量别钉进闸门脚本。早期负测把assert version v2.8.6写死v2.8.7 一发布正例先崩。现在一律从build_meta.json取值并把允许改动的字段集合当断言——比钉值更严。-webkit-backdrop-filter在 Chromium 里 computed 值是none像素逐位不变。想验证毛玻璃补丁Chrome 截图说明不了任何问题只能用 WebKit 内核看且 headless WebKit 不绘制该滤镜——像素级观感必须真机 iPhone/Mac 确认。sha 全绿和真的生效是两回事补丁必须看一行真实产物。FAT32 U 盘 跨平台 gitcore.ignorecase/filemode/symlinks/longpaths四件套要在每个仓库配好.gitattributes强制文本 LF否则 Windows 和 Linux 交替开发会把仓库搅成粥。十、复盘这个项目从一个很朴素的问题开始校园里到底有多少只猫长什么样住在哪8 月普查、9 月发版现在它是 76 只猫的星空档案也是任何学校都能 15 分钟复刻的开放模板。回头看整个项目的技术决策没有一个超出现代前端的基本盘——Canvas 2D、离屏层、分片懒加载、Web Audio、确定性算法——真正花时间的是把每一层都钉死数据有证据链、渲染有字节基线、发版有闸门、线上有核对。下一个迭代是把同学回传的校准数据合并进去让 76 颗星逐颗归位到实测坐标。项目入口在线体验 https://tianwenzzzh.github.io/meow-starmap/nuc/ 开源模板在 GitHub 搜索TianWenzzzh/meow-starmap含 15 分钟复刻教程、普查拍摄规范与跨校落地手册本文所有数据76 只在编、194 张照片、测试项数、性能实测截至 2026-09 的 v2.9.1 版本。猫咪昵称与军衔均为趣味设定不涉及任何真实人物。
