人类文明里程碑 · 活页编年史

一个以 Three.js 为核心、艺术化呈现世界历史里程碑的三维网站。它以一只活页本为隐喻,把历史中里程碑式的人、事、物登记成一张张两面活页:正面是登记卡(编目号 / 时间 / 地区 / 名称 / 描述),背面是程序化生成的"铅笔素描"。

三种视图

视图 内容
活页本 皮革封面 + 金属活页环 + 全部词条双面活页(正面登记卡、背面素描),翻页动画、纸张弯曲、堆叠阴影
时间轴 全部词条活页沿金色时间轴等距排开,上方常显主题、轴下方常显时间;点击/左右键选中并居中 + 翻面(纸张弯曲动画)
地理 Cesium 三维地球:卫星影像 + 真实地形 + 国界/城市标注 + 词条便签标记

地理视图(Cesium)

地理视图使用 Cesium(懒加载,进入该视图才加载脚本),免费、无需 Ion token:

  • 影像/地形:ESRI World Imagery 卫星影像(限制 ±85°)+ Esri World Elevation 3D 真实地形。
  • 国界:Natural Earth 110m,黑色单线。
  • 国家/城市名:Natural Earth(10m 城市点,直接使用其 name_zh 中文名),黑边黄字(FILL_AND_OUTLINE),按距离淡出,背面经深度测试隐藏。
  • 词条便签:DOM 覆盖层(非 billboard),贴地形高度,逆时针 45° 斜放、平滑曲线箭头;未选中金底黑字带黑边,选中黑底金字。
  • 交互:左键旋转、滚轮缩放(500 m ~ 8000 km)、右键原生 tilt(松手不回正)、中键回正(flyTo 动画)。
  • 画质:按设备像素比渲染(高分屏清晰)、FXAA + MSAA、地形 maximumScreenSpaceError=2.0。
  • 兜底:丢弃 ESRI 灰色 "Map data not yet available" 错误瓦片,自动回退父级放大。

技术要点

  • Three.js(ES 模块 + Vite)渲染全部三维内容,OrbitControls 供旋转/缩放。
  • 程序化纹理:纸张、皮革、活页孔、登记卡、素描全部由 Canvas 2D 即时生成,无需外部图片。
  • 程序化素描:53 种手绘风格速写(化石、泥板、金字塔、人像、神庙、王冠、斗兽场、卷轴、骑士、帆船、地球仪、苹果、蒸汽机、自由钟、进化树、原子、和平鸽、火箭、书、桥、旗帜、天平、剑、头盔、齿轮……),带抖动线条与排线阴影,同一词条每次渲染结果确定一致。
  • 双面活页:正面用 FrontSide、背面用 BackSide(内容预先水平镜像),配合纸张柔性弯曲,翻页时自然显露背面素描。
  • 数据(src/data/entries.js)包含 182 条真实历史里程碑,含经纬度、分类(人 / 事 / 物)与档案式编目号。
  • 编目号:每页登记 NO.{时间}-{地区}-{序号},如 NO.BCE3-AFETNE-I0001。时间=BC/AD 两位世纪,或 BCE1–BCE4 地质年代(1万/10万/100万/1000万年前);地区=大洲码+国家码+方位码(如 AFETNE=非洲·埃塞俄比亚·东北部);序号前缀 E/P/T/I/B(事件/人物/学说技术/文物/建筑)。
  • 时间轴交互:拖拽左右滑动、滚轮缩放;点击卡片 / 左右键 / 底部按钮 选中并平滑居中 + 翻面(带纸张弯曲动画),选中另一张时其它翻到背面的卡片自动翻回正面。
  • 文字清晰度:文字精灵 2 倍超采样栅格化;页面纹理以完整分辨率(768×1075)上传,避免降采样发糊。

性能优化(已完成)

  • 纹理懒加载 + LRU:页面 / 卡片纹理按需生成,引用计数 + LRU 释放(上限 40 页),启动只为可见窗口生成;时间轴卡片用独立低分辨率纹理池(256×358)。
  • 按需渲染:静止时停止重绘,动画 / 交互 / 状态变化时才渲染(含页面 z 过渡的 isSettling 判定)。
  • 几何降级:栈内页用极简几何(2 三角形),顶层 / 翻页页才用高分段;封面皮革曲面、活页环、铆钉均降低分段;时间轴卡片 24×6 → 12×3。
  • 双面材质合并:页面正反面合并为单 mesh(gl_FrontFacing 双纹理),draw call 减半;活页孔用 alphaTest 免透明排序。
  • Cesium 懒加载 + 后台预载:首屏不加载 6MB 脚本,首帧后后台预载,地理按钮就绪前在循环切换中自动跳过。
  • 地理数据 primitive 化:7342 城市点/标签与约 240 国家名标签由 entity 改为 PointPrimitiveCollection / LabelCollection。
  • 渲染分辨率:Three.js 像素比上限 2(min(devicePixelRatio, 2))+ MSAA 抗锯齿、Cesium 开 FXAA,三视图清晰;页面纹理关闭 mipmap、anisotropy 8→4。
  • 时间轴专项:拖拽期间暂停纹理生成(避免滑动时同步 canvas 绘制掉帧);时间轴视图关闭阴影贴图、灯光 5→2;刻度线合并为单个 LineSegments。
  • 射线检测节流:hover 合帧到渲染循环,只测可见窗口对象(binder 当前页 ±1、时间轴可视窗口)。
  • gzip:Nginx 开启 gzip(含 application/octet-stream),geojson 3MB→1MB、城市 641KB→182KB。

翻页交互

  • 空间点击翻页:按命中点的空间左右半区判定方向——书脊右侧向前翻、左侧向后翻,翻页过程中也不受页索引错位影响。
  • 松开触发:左键「松开且未拖拽(≤4px)」才翻页;拖拽旋转视角不会误翻页。
  • 首尾回卷 + END 页:最后一页再向前进入 END 页——末页翻到左侧露出其背面素描;END 页向前回卷到第一页,第一页向后直接快进到 END 页,首尾闭环,底部导航联动显示「END」。

运行

npm install
npm run dev        # 开发服务器
npm run build      # 生产构建(dist/)
npm run preview    # 预览生产构建

生成 / 切换词条数据(跑完重新 npm run build):

python scripts/gen_entries.py            # 182 条真实词条(含坐标去重、编目号、素描分配)
python scripts/gen_entries_stress.py     # 1000 条压力测试数据

部署

1. 构建

npm run build

2. 部署到 Web 服务器(hist.fukun.net)

站点托管在腾讯云 62.234.90.54(Ubuntu 24.04),静态文件放在 hugo 用户的 ~/hist 目录,Nginx 指向该目录。

通过 SSH 打包上传(密钥见 C:\Code\Claw\keys\tx-ubuntu-hugo.pem):

# Windows (Git Bash) / Linux
SSHKEY="C:/Code/Claw/keys/tx-ubuntu-hugo.pem"
tar czf - -C dist . | ssh -i "$SSHKEY" -o StrictHostKeyChecking=accept-new hugo@62.234.90.54 \
  'rm -rf ~/hist/* && tar xzf - -C ~/hist && echo 部署完成'

Nginx 配置与 SSL 证书已就绪:

  • 站点配置:/etc/nginx/sites-available/hist.fukun.net(root /home/hugo/hist)
  • SSL 证书:/etc/nginx/ssl/hist.fukun.net_bundle.crt + hist.fukun.net.key(TrustAsia)
  • 证书本地备份:C:\Code\Claw\nginx\hist.fukun.net\

3. 推送代码到 Git

git push origin main

远程仓库:git@git.fukun.net:fukunee/Hist.git(SSH)。

4. 验证

curl -s -o /dev/null -w "%{http_code}\n" https://hist.fukun.net/
# 应输出 200

操作

  • 活页本:拖动旋转、滚轮缩放;点击书页右侧向前/左侧向后(松开触发);← → / 底部按钮翻页;第一页再向后 / 最后一页再向前回卷到 END 页
  • 时间轴:拖拽左右滑动、滚轮缩放;点击卡片 / ← → / 底部按钮 选中并居中 + 翻面
  • 地理:左键拖拽旋转、滚轮缩放(右键 tilt、中键回正);点击标记选中
  • 底部导航栏按钮(或按键 1 / 2 / 3)切换 活页本 / 时间轴 / 地理 视图
  • 右下角登记卡在各视图间保持显示

目录结构

src/
  main.js              入口
  core/App.js          编排器:渲染、灯光、相机、视图切换、拾取
  scene/binder.js      活页本(封面、环、双面活页、翻页动画)
  scene/timeline.js    时间轴视图
  scene/CesiumGlobe.js 地理视图(Cesium 地球、国界、城市、词条便签)
  textures/pages.js    纸张 + 登记卡/素描两面纹理
  textures/sketch.js   程序化素描生成器(53 种)
  data/entries.js      历史词条数据(182 条)
  ui/overlay.js        HTML 覆盖层
  ui/loading.js        加载遮罩
  utils/               缓动 / 文字精灵
scripts/
  gen_entries.py         生成真实词条(坐标去重、编目号、素描分配)
  gen_entries_stress.py  生成 1000 条压力测试数据
  region_codes.py        地区编码 + 条目类型分类
  analyze/ascii/colorascii/shot/verify  开发辅助
public/
  world-countries.geojson  国家边界(Natural Earth 110m)
  world-cities-zh.json     城市点(Natural Earth 10m,中文名)

TODO / 后续规划

  • 国家/城市名贴地(参考便签):当前国家名、城市名标签在地形起伏处“钉地”不准(CLAMP_TO_GROUND 表现不理想)。需参考词条便签的做法,用 Cesium.sampleTerrain 显式采样地形高度后设置标签锚点高度,使其真正贴合地形表面。
  • 纹理懒加载:已完成(见「性能优化」——可见窗口按需生成 + 引用计数/LRU 释放)。
  • 活页本分册:为全部词条增加分册(分卷)机制——按历史时期或主题分组,顶部提供分册导航与切换,活页本只展示当前分册的内容,降低单册厚度、便于浏览。
  • 时间轴时期划分:时间轴轴线上标注中西方历史时期刻度(如中国朝代:夏商周 / 秦汉 / 隋唐 / 宋元明清;西方:古典时代 / 中世纪 / 文艺复兴 / 启蒙运动 / 工业革命等),作为背景衬托。
  • 词条插入时间轴:词条按其 timeNumeric 真实时间位置插入时间轴(当前为等距排列),使时期刻度与词条位置相互对应。
  • 词条结构考究与排版优化:重新审视 entries.js 数据模型(补充年代精度、文明圈、影响、关键词等字段),并优化登记卡正面排版(信息层级、留白、装饰细节)。
  • 性能进一步优化:继续排查渲染 / 显存 / 加载瓶颈(已做:纹理懒加载、按需渲染、几何降级、双面材质、gzip 等)。
  • 首尾边界回卷:活页本第一页 / 最后一页 / END 页边界已处理,首尾闭环。
  • 渲染清晰度:像素比上限 2 + MSAA / FXAA 抗锯齿,三视图不再发糊。
S
Description
No description provided
Readme
3.5 MiB
Languages
JavaScript 72%
Python 24.6%
CSS 2.2%
HTML 1.2%