89 lines
5.6 KiB
Markdown
89 lines
5.6 KiB
Markdown
# 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.*
|