# 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.*