Files
2026-08-04 03:49:31 +08:00

89 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Broken Time — Roman Clock
> 一块停摆的罗马钟,等待一记敲击。
> 指针仍在转动,表盘已经碎裂;时间在裂缝之间流淌。
`Broken Time` 是一款纯浏览器交互式罗马时钟。点击表盘将其击碎,碎片带着泛光四散飘落;再点击任何一块碎片,还能继续把它砸得更碎——直到每一块都小得无法再碰。时间指针始终运转,透过层层裂缝,隐约可见背后如星屑般散落的诗句。
**v0.7.3 — struck**
## 特性
- **罗马数字表盘**:XII · I · II · III · IV · V · VI · VII · VIII · IX · X · XI,环绕 60 格分钟刻度(整点刻度加粗加长)
- **实时走时**:时、分、秒三针按真实时间平滑运转;秒针以暖金色区别于银白双针,双层描边模拟指针泛光
- **击碎交互**:点击表盘触发扩散打击环动画,表盘按点击点呈扇形碎裂为 5 块多边形碎片
- **递归粉碎**:任意碎片可被再次击碎,射线切割、锯齿裂边,越碎越小,直到面积过小无法继续
- **诗意文字碎片**:表盘背后密布 40+ 条与时间、破碎、记忆、死亡相关的诗句,随碎裂逐层显现
- **碎片物理**:初速度、角速度、空气阻尼衰减、面积加权的碰撞分离,破裂边缘带三层泛光消散
- **复古拟物细节**:自定义指针光标、BSOD 风格四角角标、逐字呼吸的提示语、`Reform Time` 复原按钮
## 使用方法
用任意现代浏览器打开 `BrokenClock.html` 即可,无需构建、无需安装、无外部运行时依赖(仅 Google Fonts 字体为网络加载,缺失时自动回退到衬线字体)。
| 操作 | 效果 |
| --- | --- |
| 点击完整表盘 | 击碎时钟,碎片飞散 |
| 点击碎片 | 继续粉碎该碎片 |
| 点击 `Reform Time` | 恢复完整表盘 |
## 交互设计
- **打击感**:每次敲击都有一个约 130ms 的打击环动画——双层同心圆扩散、透明度与线宽同步衰减,随后碎片才开始崩解,先声后效,节奏分明
- **碎片排序**:初次碎裂时 5 块碎片按面积降序排列,第二大的一块被标记为「文字碎片」,承载诗句贴图,保证诗意的最高可见度
- **越碎越慢**:碎片初速度按序号递增缩放(`speedFactor = 0.4 + i * 0.1`),整体呈扇形展开,避免一拥而散
- **弹性碰撞**:相邻碎片按面积比例互相推挤(质量正比于面积),并施加极小速度分离项,防止碎片粘连
## 技术实现
### 渲染架构
- 纯原生 **JavaScript + Canvas 2D**,单文件、零依赖
- **三层画布分离**:
- `clockCanvas`(离屏):完整表盘静态层——罗马数字、刻度、指针,每帧重绘
- `textCanvas`(离屏):诗句层,按表盘圆形区域以螺旋式行排布,仅窗口缩放时重绘(`textDirty` 标记)
- 主 `canvas`:交互合成层,负责碎片变换、碰撞、打击效果
- 碎片绘制时 `save → translate → rotate → translate → clip → drawImage`,将对应离屏贴图按碎片自身变换裁剪映射,实现「破碎表盘」视觉
### 碎裂算法
1. **初次碎裂**:以点击点为公共顶点,按随机加权区间(`0.6 + random * 1.4`)将圆周角切成 5 个扇形,每个扇形的两段半径用 `zigzagPath` 加锯齿扰动,外弧用 30 段离散点逼近
2. **递归碎裂**:对目标碎片发射 3–4 条随机射线,用 `rayPolygonIntersect` 求射线与多边形边的首个交点(`t > 1e-6, u ∈ [0,1]` 的最近者),按交点角度排序后依次连接成新的子多边形,面积小于 50 的碎屑直接丢弃
3. **顶点数据**:每个碎片携带局部坐标系下的多边形点集、质心、面积、等效半径 `√(area/π)`、线速度、角速度,旋转时通过 `pointToScreen / pointToLocal` 双向换算命中检测
### 物理与效果
- **运动积分**:每帧 `offset += v`、`rotation += ω`,速度阻尼 `v *= 0.95`、角速度阻尼 `ω *= 0.97`,碎片逐渐静止
- **碰撞解析**:O(n²) 对撞检测,基于等效半径的软球模型,按面积加权分配推挤位移,模拟质量差异
- **泛光模拟**:刻意规避 `shadowBlur`(性能黑洞),改用三层描边叠加——外层粗线低透明度模拟弥散、中层中等粗细做光晕、内层细线高透明度做核心高亮,透明度随碎片诞生后 1.5s 内指数衰减
- **指针泛光**:同样以双层描边(宽晕 + 细芯)替代 shadow
### 关键函数
| 函数 | 职责 |
| --- | --- |
| `drawClockTo(c)` | 表盘静态层渲染:罗马数字、刻度、三针 |
| `drawTextShardTo(c)` | 诗句层渲染:按圆截面逐行布字 |
| `shatterInitial(x, y)` | 初次扇形碎裂 |
| `shatterShard(shard, x, y)` | 递归射线切割 |
| `rayPolygonIntersect(...)` | 射线与多边形求交(切割 + 命中) |
| `zigzagPath(...)` | 锯齿化破裂边线 |
| `resolveCollisions()` | 面积加权碰撞分离 |
| `pointToScreen / pointToLocal` | 碎片局部/世界坐标换算 |
## 文件结构
```
BrokenClock/
└── BrokenClock.html # 全部实现,单文件即完整应用
```
## 主题与彩蛋
- 四角角标致敬 Windows BSOD:`0x0000007B`、`SESSION: FRAGMENTED`、`ROMAN.PROTOCOL // XII`
- 诗句取材于时间的辩证:`Time is the fire in which we burn.`、`Memento mori.`、`Tempus edax rerum`、`And in the end, the clock was always already broken.`
- 字体三件套:`Cinzel`(罗马铭文体)、`EB Garamond`(诗句衬线体)、`Special Elite`(打字机角标体)
- 光标是一支 SVG 手绘的敲击锤,命中即锤落
> *Strike the dial — shatter the lie — free the hour.*