把 ACR 时间轴搬进浏览器:WebTimeline 开发日记
从一份难以维护的时间轴 JSON,到一个能浏览、导入、编辑、校验并重新导出的可视化工作台。
WebTimeline 目前仍处于早期开发阶段。本文记录的是从首个原型到可用编辑闭环的过程,不代表最终产品形态。
为什么要做 WebTimeline
PromeRotation 的时间轴很强,但写一条真正能用的轴并不轻松。
过去,时间轴往往由 ACR 作者或熟悉插件结构的玩家直接维护 JSON。一个技能为什么在这里触发、属于哪个阶段、目标是谁、是否会撞 CD,很多信息都藏在字段、条件和嵌套结构里。对普通玩家来说,哪怕只是把一个减伤向前挪几秒,也要先理解整份配置。
我想解决的不是“如何再写一种时间轴格式”,而是把已有格式变成一个可以看见、可以拖动、可以检查的界面:
- 新手可以直接看 Boss 白轴和 ACR 模拟结果。
- 想微调的人可以拖动技能、修改时间和指定目标。
- 原有时间轴能够导入,编辑后还能回到插件继续使用。
- 复杂逻辑留给系统处理,用户只需要面对时间与技能。
这就是 WebTimeline 最初的目标:把偏代码的写轴过程,变成一个真正的时间轴编辑器。
第一阶段:先让时间轴“看得见”
2026 年 7 月 8 日,WebTimeline 的第一个原型进入仓库。
最开始的版本已经能把 Boss 事件、玩家技能和阶段信息铺到横向时间线上,但它更像数据展示页,还不是编辑器。技能密集时会重叠,Boss 读条、伤害判定和结束时间挤在一起,右侧栏目也占用了太多主时间轴空间。
第一轮工作没有急着增加更多页面,而是先确定主界面的信息结构。最终,主时间轴不再按“导入、手动、ACR”这些数据来源分栏,而是按实际用途组织:
- Boss 读条与伤害
- 起手
- 输出轴
- 减伤与奶轴
- 爆发
- QT 控制
- ACR 模拟
- 关注技能
来源仍然会显示,但只作为标签和样式。因为对编辑者来说,最重要的问题是“这个技能做什么”,而不是“它从哪一段代码里来”。
时间轴同时加入了总 P 与 P1 至 P5 的切换。每个阶段都使用自己的时间范围,技能在阶段内显示相对时间,底层仍保存整场战斗的绝对时间。这个约束后来也成为拖动编辑的基础:P3 的技能只能在 P3 的范围里调整,不能因为一次拖动跑到相邻阶段。
第二阶段:兼容真实的 PromeRotation 时间轴
真正开始导入用户文件后,很快遇到了第一个大问题:所谓“时间轴 JSON”并不只有一种结构。
项目目前需要同时处理三类数据:
- 传统 PromeRotation 触发轴。
- PTL 时间轴。
- WebTimeline 自己的项目格式。
传统触发轴包含 Meta、Root、触发条件、并行分支和动作节点;PTL 更依赖锚点、偏移量和阶段结构。如果把两种格式都简单地递归展开,结果看起来能显示,实际时间却可能全部错位。尤其是 PTL 的技术锚点,并不一定等于用户看到的分 P 边界。
为此,导入层被拆成了两步:先识别格式,再归一化成编辑器内部事件。内部事件至少包含技能 ID、名称、时间、阶段、分类、来源、持续时间和目标等信息。缺失字段会补默认值,无法识别的职业和 ACR 也不会阻止浏览。
导出则保留了两条路径:
- 导出原生 PromeRotation 时间轴,尽量保留导入时的 Meta、Root 或 PTL 原始内容。
- 导出 WebTimeline 项目文件,完整保存当前编辑状态,方便再次导入继续工作。
这部分最重要的原则是“往返不丢数据”。WebTimeline 不能只负责把文件吃进去,还必须让编辑后的结果能够安全回到原来的工作流。
第三阶段:把浏览器变成真正的编辑器
时间轴能显示之后,下一个问题是如何避免误操作。
WebTimeline 默认进入浏览模式。这个模式下,技能不能拖动,点击技能只会查看详情或在右侧总览中定位。切换到编辑模式后,用户手动技能和允许编辑的导入技能才会解锁;Boss 技能与 ACR 自动模拟技能保持锁定。
这个区分看似简单,实际影响了几乎所有交互:拖动监听、删除按钮、时间输入框、技能插入面板和目标选择器都必须服从当前模式。
悬浮技能插入面板
插入技能被做成了只在编辑模式启用的悬浮工具。面板本身可以移动,避免挡住正在编辑的阶段;技能库只显示当前职业相关技能,并按全部、输出、减伤、爆发药、60 秒爆发、120 秒爆发和 QT 分类。
技能 ID 输入也被保留,但不再要求用户重复填写技能名。输入有效 ID 后,编辑器会自动匹配数据库中的名称和图标。
拖动不是“改一个 left 值”
拖动功能经历了很多次返工。
最初使用浏览器原生 drag and drop,遇到长条减伤时并不稳定;拖动过程中重新渲染整个时间轴,还会让滚动位置突然跳动。后来增加了 Pointer Events 路径,并把高频辅助线更新放进 requestAnimationFrame。拖动时缓存当前轨道和时间换算上下文,只更新定位线、时间气泡与相邻事件差值,松手后再提交数据。
现在拖动一个技能时,界面会同时显示:
- 当前准备落下的准确时间。
- 与附近技能相差多少秒。
- 所在阶段允许的时间边界。
- 当前轨道是否接受这一类技能。
这更接近视频剪辑软件的操作感,而不是盲猜一个百分比位置。
CD 校验必须符合游戏逻辑
只允许拖动还不够。用户如果能在技能 CD 没转好时继续插入,编辑器生成的轴就没有意义。
WebTimeline 会根据技能 ID、共享 CD 键、充能和复唱时间检查队列。如果插入时间不合法,手动队列会提示冲突或将技能顺延到可用时间。检查已有技能时,还必须排除正在移动的技能本身,并且不能让未来出现的同名技能反过来阻止当前技能向前移动。
起手也必须参与 CD 基线。否则一个在倒计时或开场已经使用的技能,会在进入 P1 后被错误判断为立即可用。这类问题往往不会造成页面报错,却会让整条轴失去可信度,因此后来专门补了回归测试。
第四阶段:减伤不只有时间,还有目标
输出技能的目标通常可以默认为 Boss,但减伤、治疗、无敌和支援技能不能这样处理。
例如,给自己的无敌、给队友的单体减伤、以 Boss 为目标的雪仇,虽然都可能出现在“减伤”栏目里,目标规则完全不同。WebTimeline 因此把目标变成了时间轴事件的一等字段:
- 攻击技能默认指向 Boss。
- 减伤、治疗和无敌要求确认目标。
- 支援类减伤不能选择 Boss。
- 从外部导入的减伤也可以在详情栏重新编辑目标。
拖入需要目标的技能后,目标选择面板会跟随刚刚放下的技能出现。为了避免被时间轴容器裁切,它最终改成固定定位的浮层,同时在重新渲染时保留横向滚动位置。
这是一个很小的 UI,却连接了导入、编辑、分类和最终导出四个环节。少了它,时间对了,实际执行对象仍然可能是错的。
第五阶段:把 ACR 数据接进来
WebTimeline 不是独立于 PromeRotation 的另一个技能表,它需要理解当前职业和当前 ACR。
项目的数据生成流程会读取 PromeRotation 本体源码、玩家上传的 ACR 包以及必要的解析产物,再生成前端可以使用的 ACR 数据库。数据库记录职业、ACR 名称、作者、支持状态、技能、QT 控件和生成时间。
在此基础上,编辑器可以:
- 根据导入轴自动识别职业和 ACR。
- 为当前职业提供技能插入列表。
- 在没有手写输出轴时生成 ACR 模拟预览。
- 从 ACR 中发现 QT 控件,而不是只为某一个作者硬编码。
- 区分 ACR 模拟、导入事件和用户手动事件。
QT 最初散落在输出和爆发栏目里,后来被统一成独立轨道。时间相近的一组 QT 状态会收纳到一个图标中,点击后再查看具体开关,避免一排重复控件挤满时间轴。
60 秒与 120 秒爆发也不再是页面外的独立设置,而是作为“爆发包”参与主时间轴。新手看到的是开始时间、技能数量和来源;需要细调时再展开查看内部技能。
第六阶段:关注、追踪与整页总览
“关注技能”和“定位技能”曾经一度混在一起。
现在它们被定义成两种不同操作:
- 关注技能是一份持续存在的追踪列表,会汇总同一技能在整条轴中的所有出现时间和来源。
- 追踪是一次性定位。点击右侧技能,主时间轴中的对应技能会高亮;点击主时间轴技能,则会反向展开右侧总览中的对应事件。
右侧栏因此只负责浏览与追踪,不承担技能插入。栏目统一为“整页总览”,Boss、起手、输出、减伤、爆发和 QT 都可以折叠。处于某个分 P 时,追踪优先在当前阶段寻找,不会无缘无故跳回总 P。
时间轴左侧还增加了轨道可见性控制。用户可以临时隐藏 Boss、输出、减伤或爆发等长条,把空间留给当前要检查的内容。
第七阶段:从妖星扩展到更多绝本 Boss 白轴
WebTimeline 第一阶段围绕妖星乱舞绝境战开发。随着编辑器本身逐渐稳定,Boss 白轴数据开始扩展到更多绝本,目前已经接入妖星、绝巴哈、绝亚历山大、绝龙诗和绝伊甸的基础时间线数据。
这部分数据主要依据 FFLogs 战斗事件整理,但“抓到事件”不等于“得到正确白轴”。
一开始只把 Boss cast 与对应伤害包匹配,结果会漏掉两类重要事件:
- 没有对应读条、直接产生伤害的 Boss 技能。
- 没有伤害数值、但决定机制判断的动作与静止类事件。
现在的数据构建会合并三类记录:
- 读条与对应释放伤害匹配后的事件。
- 没有匹配读条的 Boss 伤害事件。
- 伤害为 0、但对机制时间线有意义的标记事件。
同时过滤自动攻击、DoT 汇总、纯视觉动作、友方来源和明显噪声。新增的技能名称会进入本地中文映射,避免时间轴上混杂英文原名。
Boss 头像则采取了保守策略:只有项目里存在正确素材时才显示。新绝本暂时没有对应图片,就不使用妖星 Boss 头像轮换冒充,等真实素材准备好后再接入。
第八阶段:UI 从“功能都在”走向“可以长期使用”
WebTimeline 的界面重做过不止一次。
早期页面分割很重:左侧导航、右侧工具和顶部信息各占一块,主编辑器反而被挤窄。后来逐步移除说明性大卡片,把导入、导出、职业、ACR、模式和副本信息收进一条浅色顶部工具栏;左侧只保留轻量图标轨道,右侧只留下整页总览。
当前设计采用温暖的浅色背景、细边框、低圆角和克制的阴影,整体参考 Claude / Anthropic 一类工具界面的气质。它不是为了做成一张漂亮海报,而是为了让用户连续盯着几百个时间节点时仍然能分清层级。
几个看起来不起眼、实际反复调整的细节包括:
- Boss 读条、释放、伤害和结束信息不能互相遮挡。
- 持续型减伤保持与真实时长成比例,但文字不能被长条拉伸。
- P5 末尾事件必须在轨道边界内截断,不能突出容器。
- 时间很近的玩家技能需要视觉错层,不能叠成一张卡。
- 顶部迷你导航条必须始终可见,不能因为长时间轴滚到页面底部才找到。
- 窄屏允许横向浏览,但按钮和文字不能被压到不可读。
这些工作很难用一个“大功能完成”来概括,却决定了编辑器到底是演示品还是能每天使用的工具。
测试与部署
时间轴编辑器的问题很多都属于“看起来差不多,数据其实错了”。因此项目逐步补上了围绕真实行为的回归测试,覆盖内容包括:
- 传统触发轴与 PTL 格式识别。
- 阶段锚点、相对时间与绝对时间换算。
- 导入后再导出的数据保留。
- 编辑与浏览模式隔离。
- 指针拖动、滚动保持与放置辅助线。
- CD 冲突与起手基线。
- 减伤分类、目标限制和目标选择器。
- ACR 模拟、QT 控件与爆发包。
- Boss 读条、伤害、零伤害机制标记与中文名称。
- 右侧追踪、反向定位和关注技能汇总。
- 时间轴边界、卡片防重叠和响应式布局。
截至本文整理时,全量测试为 286 项,全部通过;静态构建也已通过。网站使用静态构建部署到 Cloudflare Pages,入口分为主页与编辑器,核心数据在构建阶段生成,浏览器端不需要保存任何私密 API 凭据。
这段开发里最重要的几个认识
1. 时间轴编辑器首先是数据转换器
拖拽只是表面。真正困难的是在 PR、PTL、ACR、Boss 事件和 WebTimeline 内部模型之间保持语义一致。只要阶段、目标或来源丢了一项,后面的漂亮界面都无法补救。
2. 来源不应该成为主分类
“手动”“导入”“模拟”适合做标签,不适合成为三条互不相干的时间轴。用户调整的是战斗计划,而不是数据管道。
3. 编辑自由必须受游戏规则约束
编辑器可以让技能自由移动,但不能允许不可能的 CD、错误目标或跨阶段漂移。一个可信的限制,比一个什么都能拖的画布更有价值。
4. 真实样本比想象中的格式更复杂
黑骑轴、白魔轴、PTL、玩家 ACR 和 FFLogs Boss 事件不断暴露边界情况。每次修复都应该留下样本与测试,否则下一次改 UI 时很容易把旧问题带回来。
5. UI 的空间本身就是功能
时间轴越长,主编辑区域越珍贵。每增加一个侧栏、说明框或大标题,都在减少用户观察技能关系的空间。最终的界面应该安静地托住数据,而不是抢走注意力。
现在做到哪了
目前 WebTimeline 已经形成一条可用的核心闭环:
选择副本与职业
-> 导入传统触发轴 / PTL
-> 浏览 Boss 白轴与 ACR 模拟
-> 切换编辑模式
-> 插入或拖动技能
-> 校验阶段、CD 与目标
-> 追踪关键技能并检查整页总览
-> 导出原生时间轴或 WebTimeline 项目
它仍然是早期版本。ACT 与 FFLogs 对比、伤害估值、更完整的职业 ACR 支持、多版本本地存储、分享展示页、手机端和八人团队协作,都还有很长的路要走。
下一阶段会优先继续提高数据可信度:补全更多绝本技能与中文名称,完善不同职业的 ACR 模拟,继续验证 PTL 和传统轴的往返兼容,并让减伤、QT 与爆发编辑更接近插件里的真实执行结果。
写在最后
WebTimeline 最初只是一个很直接的念头:既然时间轴本来就是“什么时间做什么事”,为什么不能像剪视频一样把它摆出来?
开发到现在,我越来越确定,可视化并不是给复杂系统套一层好看的外壳。它真正做的,是把隐藏在 JSON、触发条件和作者经验里的规则,变成用户能够观察、理解和修改的东西。
第一版离完成还很远,但那条从“导入一份看不懂的轴”到“在浏览器里拖动、校验并导回插件”的路,已经走通了。
接下来,就继续把它磨成一个真正愿意拿来写轴的工具。