Skip to content

回应时序 · 动画:参考、数据、故事与实现 ​

状态:已实现(2026-10-20,分支 codex/motion-timing)。前三步(参考、数据、故事)审阅通过后照做,第四到六步(motion、direct、ship)的取舍与验证记在文末第 4–6 节;审阅定下的三处(原文末“待定”)照审阅改了正文。

2026-10-19。按第三轮的六步流程(reference → data → story → motion → direct → ship),给 回应时序做前三步,在任何东西动起来之前定下一个点、一个英雄数字和三拍。范本是份额拨盘与整理脉冲的三份记录(拨盘 spec · story、脉冲 spec · story);这张图现在怎样工作见打磨记录 A。前三步不改任何代码与数据,数字都从同一份回放算出,口径写在第 2 节。

1 参考:这张图现在怎么讲“开了多久” ​

现在怎么讲 ​

  • 长度就是时长。 每条回应路径一个六边形格,左端是登记那一轮,右端是关上那一轮(还开着就延到此刻线)。横轴是真实时间,1440 宽时每小时约 2.3 px:12 小时一轮约 27 px,开了 240 小时的格约 540 px。
  • 关法靠线型。 白底黑框是开着,灰底是答后关上,虚线是改写后失效,灰框是换成新问题,末端竖线是随事项撤下或并入。
  • 期限是一个倒三角。 取登记时的期限(问题自己的 due_at,没有就取条目的),过期后还开着的那段加斜线。
  • 数字只在旁边。 “已开 N”只出现在悬停提示和右栏(右栏只列此刻开着的);关上的格开了多久,要悬停才读得到。
  • 播放是一步一步跳。 每 1.3 秒切一帧,此刻线用 260–520 毫秒补间走过去;格子的开闭、右栏卡片在补间开始的那一刻就换成新帧,比此刻线先到(打磨记录 A“未解决”第一条)。

哪里讲不动 ​

  1. 看不出“一直在等”。 一个格从左到右一次画好,开了 10 天和开了 12 小时只是长短不同;等待本身没有被演出来,两周里涌进来的输入也只是顶上一排小柱。
  2. 看不出被问了几次。 母亲九月住哪登记了 14 次(条目每改写一次就以同一问题重新登记),格上看不到,只有详情的开闭记录里逐条写着“条目改写,以同一问题重新登记”。
  3. 看不出是谁关上的。 线型只说“没等到你的回答”,不说是别人先开了口:派对那件是 Kajsa 替全家报了人数,母亲那件是她自己交了回执。这两件事在回放里有据可查(关上它的那条声明的 reason 与 sources),图上一个记号也没有。
  4. 小时以下的先后挤在一个像素里。 问题 12:00 失效、你 12:30 发短信给母亲,两件事在图上相距约 1 px。复盘最想看的往往就是这种“晚了一步”。
  5. 期限只记第一次。 条目改写时期限也会移,比如“Smilla 多吃了药:打诊所?”的条目期限从没有,移到 8/12 16:00、8/13 16:00、8/14 08:00,图上只画登记时的那个(这里是没有)。概念卡给灰色留的含义里本来就有“已被改掉的旧期限”,完整版没有实现。
  6. 两只钟没有一起走。 “已开多久”往上数,“离期限多久”往下数,两者之和不变。右栏把它们写成一行小字;图上格的长度和格尾到倒三角的空白就是这两只钟,却都没有数,也看不出它们一长一短地对着走。

从拨盘与脉冲搬什么 ​

要素拨盘或脉冲的做法回应时序理由
游标只停在一件上拨盘:实心游标只停在要紧、等你回的那一件,没有就空心退回硬止刻搬,改颜色和规则这里朱红只给你的回应,游标用墨黑。故事里停在英雄问题上,落定后停在右栏第一件(期限最近,没有期限的按开得久);没有开着的问题时空心停在此刻线顶端
切点后 0.4 秒没有焦点拨盘每轮切点后 0.4 秒游标才落只在拍与拍之间搬第 2 拍每轮只有 0.5 秒,逐轮等 0.4 秒会拖成慢放;每拍开头留 0.4 秒
一帧硬切,过冲约 3.7%拨盘的扇区、游标;脉冲的展开离散量搬,此刻线不搬登记、重新登记、关上、改名、计数都在此刻线到达批次刻度的那一帧硬切;游标上下移动与 8 px 弹出带过冲。此刻线只缓出、不过冲:时间过冲就是倒流
一件事同时说三遍拨盘切点上标题卡、进度、游标同帧换搬关上时,格的线型、右栏或面板、登记方块同一帧变
整屏先压暗,再让唯一亮点出现脉冲第 3 拍压暗 0.3 秒,朱红线成为唯一亮点搬节奏,不搬含义英雄问题关上那一刻其余泳道先压暗 0.3 秒;随后唯一的亮点是关上之后才落下的那道朱红短刻
朱红线 = 新登记的问题脉冲:要你开口的那一件从枝梢打一道朱红线不搬回应时序的朱红从草图起就只给你的回应;问题格画成朱红,“问了你”和“你回了”就同色了
焦点最慢脉冲:闪电全片只改道两次搬第 2 拍里英雄泳道不闪、不跳,只是变长;动的是周围的格子开开关关
一颗一条脉冲:云里一颗亮粒子就是一条输入搬到概念层批次条里一粒灰点就是一条输入,落在它自己的时刻上
一轮一次心跳脉冲:每轮 4.25 秒;拨盘每轮 4 秒不搬这里的单位是时长不是轮:动画时间与真实时间等比(12 小时 0.5 秒),story 数据集的不定时间隔要原样看得出来
循环、一直动拨盘 20 秒循环,脉冲连播首尾相接;概念层不停不搬复盘页不循环;落定后整页静止,等人读
暗底辉光脉冲不搬沿用草图的纸白底与墨线(kit.mjs、live.css)

概念层占比:约一成,只在时间走的时候动 ​

拨盘和脉冲是官网首屏,概念层占八成;回应时序是复盘与催办的工具,我把概念层压到一成以内,信息层占九成以上。理由:

  • 这是读的页面。 composite 一屏 21 条泳道,格里有问题、旁边有期限与刻度,人是带着“我欠谁什么”来查的;背后有东西在动,字就难读。
  • 灰色在这里已经有含义。 已关上的格、中线、撤下期间的虚线都是灰的;概念层的灰一旦进泳道,就会被读成“已关上”。
  • 时间本身就是动画。 此刻线在走、格在变长,这是信息层自己的动作;概念层只需要把“这段时间里涌进来很多东西”垫在底下。

概念层只有两样,都在泳道之外:批次条里的灰点(一粒一条输入,落在它自己的时刻上,到批次刻度时扫进那一轮的柱子),和此刻线身后约 6 小时宽、0.6 秒衰减的浅灰拖尾。它只在此刻线走的时候动;悬停、展开、落定后都静止;prefers-reduced-motion 时不画。

2 数据:两周里的回应路径 ​

这张图用的是哪套数据、哪一帧 ​

  • 默认是 composite 的 f26。 ?data= 不写时用 composite(data/select.mjs 的 defaultDataset);不带 ?frame= 时,kit.mjs 的 chosenFrame 依次看地址栏、story 的 storyIndex、数据集的 sketchFrames,composite 的 scenario.sketchFrames.timing 是 'f26'(8/16 00:00),旁白取 sketchNotes.timing:“车道下的水管、#13596、开学同意书、VAB、三个旧 PR 同时开着,猫的药量与母亲住哪已先后关上”。画廊卡片显示 sketchFrames[id] || frame,所以写的是“默认帧 f26”。
  • catalog.mjs 的 frame: 'f30' 是 story 的默认帧。 注释写明 frame is the story default;timing.mjs 传 storyFrame: 32,story 的第 32 帧就是 f30(10/23 09:40)。只有 ?data=story 时才用到它。CONCEPTS.md 的第二轮概念卡是为 story 写的,所以写的是 f30。

口径 ​

和页面用同一套推导:live.mjs 的 askPaths、pathStatus、dueOf。

  • 一条回应路径 = 一个 ask.id。开于第一次出现在 frame.asks 的那一帧;之后条目每改写一次、这一帧又登记了同一 id,算一次重新登记。条目撤下或并入,或者改写后这一帧没有重新登记,就在那一帧关上。
  • 关上的方式:此前有 event.feedback[].ask 指向它的,算“答后关上”;否则按结局分成改写后失效、换成新问题、随事项撤下、随事项并入。
  • “开了多久”= 关上那一帧的 at 减登记那一帧的 at,还开着就算到最后一帧;“轮” = 两帧序号之差。
  • 期限 = ask.due_at,没有就取登记那一帧条目的 due_at。
  • “你的回应” = event.feedback[];带 ask 的是回答,不带的是只记在这件事上的动作(图上的短刻度)。动作句用 actionSentence。
  • 交叉核对用 stage.mjs 的 askLife(拨盘与脉冲用的那套,只认此刻在根树上、版本对得上的问题)。两套在 composite 上逐帧一致,在 story 上只差一处,见本节末。
  • 计算方法:用 engine.mjs 的 replayScenario 重放,照抄上面两套推导,逐帧逐路径列出。脚本是一次性的,不进仓库。

composite(默认,Jonas 的两周) ​

29 帧,f00 8/3 00:00 到 f28 8/17 00:00(Europe/Stockholm),每 12 小时一轮。

量值来源
回应路径26 条:答后关上 11、改写后失效 3、随事项撤下 2、随事项并入 3、到 f28 仍开着 7;没有“换成新问题”不同的 frames[].asks[*].id;pathStatus(path, 28)。与 拨盘 data.json 的 asks(answered 11、lapsed 8 = 3 + 2 + 3)一致
你的反馈160 条,其中 32 条回答了登记的问题,全部在来源处(via: source)event.feedback[]、link.ask、link.via
开得多久答后关上的 11 条中位开 36 小时(12–168);没等到回答就关上的 8 条中位开 156 小时(12–252)关上帧 at − 登记帧 at
第一次回应答后关上的 11 条,从登记到你第一次回应中位 11.6 小时第一条 feedback.ask 的 event.at − 登记帧 at

两周里开得最久:“派对回几个人?”,21 轮、252 小时,没有答掉,随事项撤下。

  • ask-02,条目 kraftskiva-22-8,短问法“派对回几个人?”。f01(8/3 12:00)登记,ask.due_at 8/12 23:59,登记 6 次(f01、f02、f14、f18、f20、f21)。
  • 两周里你在这件事上一条反馈也没有(event.feedback[].target 没有一条指向它)。8/12 23:59 期限过了,f20 条目改名为“Kräftskiva报名12日已截止未回”。
  • 8/13 12:30,Kajsa 在派对群里替全家报了 4 人(f22 的事件,比期限晚 12 小时 31 分)。
  • f22(8/14 00:00)Agent 撤下这件事,声明的 reason 是“Kajsa已报4人,Joakim已确认……这件事不用你再做”,问题随事项撤下。从登记到关上开了 252 小时,最后 24 小时 01 分是过期还开着。

第二久:“母亲九月住哪?”,20 轮、240 小时,没有答掉,改写后失效。 这是第 3 节的英雄问题:

时刻帧发生什么字段
8/4 12:00f03登记“母亲九月住哪?”(expects: choose,选项:住我们家 / 去临时公寓 / 各住一段),期限 8/14 23:59,条目 24 份frames[3].asks['mamma-stambyte'](id: ask-08、short、due_at);records[3] 的 share
8/4 21:30–22:10f04你用 iMessage 回复 Kajsa(2 次),在兄妹群里回复(1 次)frames[4].events[].feedback,target: mamma-stambyte,不带 ask
8/7 13:40f10你用短信回复母亲同上,frames[10]
8/9 21:10f14你给母亲打了电话(24 分钟)frames[14],kind: call_log
8/10 21:15f16你用 iMessage 回复 Kajsaframes[16]
8/11 13:00f18你在兄妹群里回复(原文大意:Kajsa 想让她来住两周、其余住疏散公寓,还没跟妈妈说)frames[18]
8/13 19:30f22你给母亲打了电话(12 分钟)frames[22],kind: call_log
8/14 10:50、11:05f23Ulrika 在兄妹群说母亲自己交了回执;母亲本人短信:选 6 号的疏散公寓f23 声明 mamma-stambyte 的 sources:event:fam:B14.1、event:fam:B14.2
8/14 12:00f23条目改为已办,标题“Mamma已交回执,选疏散公寓”;没有重新登记,问题改写后失效records[23];声明 reason:“降档:回执已经交了,不再需要你做什么。她没有采用来你家住两周的方案。”;frames[23].title:“打了诊所;母亲自己定了”
8/14 12:30f24你用短信回复母亲,问她还想不想来住两周frames[24] 的反馈,不带 ask
8/17 00:00f28同一件事登记了新问题“母亲 23 日来吃饭?”frames[28].asks['mamma-stambyte'](ask-26)
  • 开了 240 小时、20 轮,登记 14 次(f03、f04、f05、f08、f09、f10、f14–f20、f22),关上时离期限还差 11 小时 59 分。
  • 份额:f03–f09 与 f18–f22 是 24 份,f10–f17 是 12 份。拨盘的游标在它上面停了 19 轮(f03–f21,拨盘 data.json 的 beats.b2.cursorLongestStay)。
  • 开着的 240 小时里,你在这件事上有 8 条反馈(6 条消息、2 个电话),没有一条记成对这个问题的回答。同一段时间收到 2342 条输入(frames[4..23].events.length 之和,占两周 3196 条的 73%),其中 34 条的 relatedIds 含这件事。

最久的两件都不是你答掉的,前五久里有四件如此:第三久 ask-03“#13350 复审”204 小时,随事项并入;第四久 ask-10“要 Göran 来抬棚屋吗?”192 小时,随事项撤下;第五久 ask-06“#13473 定时器重构:批准?”168 小时,答后关上,是答后关上里开得最久的一件(你登记后 15 分钟就第一次回复,8/10 23:06 才批准)。

同时开着最多:8 件,在 f04(8/5 00:00)、f08(8/7 00:00)与 f15(8/10 12:00)三轮。 pathStatus(path, i).state === 'open' 的条数,askLife 给出同样的数,也和拨盘 data.json 的 openAsksPerRound.max 相同。f15 的 8 件:派对回几个人、#13350 复审、#13473 定时器重构、母亲九月住哪、要 Göran 来抬棚屋吗、改签直飞还是照旧、#13295 复审推到哪天、NTLM 复用修复;它们后来答后关上 3 件,没等到回答就关上 5 件。三轮里都有派对和母亲这两件。同时开着最少是 f23–f25 的 3 件:f22–f23 两轮关上 6 件,其中你答掉 2 件(8/14 08:02 给诊所打电话,答“Smilla 多吃了药:打诊所?”;11:38 批准 #13572),另外 4 件是事情自己变了。

离期限最近时被答掉的:composite 里没有一件在期限前答掉。

  • 26 条里 8 条有期限(dueOf):答后关上 1 条,晚了;没等到回答就关上 4 条;到 f28 仍开着 3 条。
  • 唯一答掉的带期限问题是 ask-05“Smilla 约上兽医了吗?”。期限 8/3 23:59 取自条目的 due_at(问题自己没有);8/5 08:07 你给 Kajsa 发 iMessage“Bokat mån 10:15”,反馈 {ask: 'ask-05', action: 'done', meaning: 'act'},比期限晚 32 小时 08 分。
  • 没等到回答就关上的 4 条:派对(期限过后 24 小时 01 分撤下)、母亲住哪(期限前 11 小时 59 分失效)、ask-15 开学同意书(期限前近 5 天并入另一件,同一问法在 ask-21 继续)、ask-18 后刹排气(期限前 80 小时失效)。
  • 如果期限按最近一次登记时的条目期限算(见第 1 节第 5 条),离期限最近的是 ask-16“Smilla 多吃了药:打诊所?”:f22 重新登记时条目期限是 8/14 08:00,你 08:02 给诊所打了电话,晚 2 分钟。
  • 两周结束时(f28,旁白“两周结束:水管还没回”),离期限最近的开着的问题是 ask-23“主水管:挖开换还是免挖?”,期限 8/17 10:00,还有 10 小时;在默认帧 f26 是还有 34 小时。
  • 同在 f28,开得最久的两件各又登记了一个新问题:派对恢复,问“派对 Swish 500 克朗”(ask-25,期限 8/21 23:59);母亲那件问“母亲 23 日来吃饭?”(ask-26,没有期限)。

story(手写情境,林苒的两周) ​

37 帧,cold-00 10/12 08:30 到 f34 10/25 19:40(Asia/Shanghai),不定时,最近两帧只隔 10 分钟。

量值来源
回应路径39 条:答后关上 11、改写后失效 17、换成新问题 7、随事项撤下 1、随事项并入 1、到 f34 仍开着 2同 composite
你的反馈15 条:回复 12、按钮 3(其中 1 次是稍后提醒,到点另有 1 条提醒);12 条回答了登记的问题event.feedback[]、link.ask、event.reminder
开得多久答后关上的中位 29.5 小时;没等到回答就关上的中位 21.8 小时同 composite
  • 开得最久: ask-19“请店长解释暂存列”,f12(10/16 10:00)到 f22(10/20 09:40),10 轮、95 小时 40 分,换成新问题,期间你没有回应。
  • 同时开着最多: 6 件,在 f01–f06、f09、f22、f23(live.mjs 还把 f10 算作 6 件,见下)。
  • 离期限最近时被答掉: ask-17“周六 11 点见妈妈,可以吗?”。f11(10/15 21:00)登记,期限是条目的 10/17 11:00;10/16 21:11 妈妈说周六临时有事、要不改周日;10/17 09:32 你回复“我和妈妈说好了,周日十一点见”(reply / change / decide),离期限 88 分钟;f15(10:00)答后关上。其次是 ask-09 早 3 小时 26 分、ask-35 早 24 小时 29 分(先点稍后提醒,到点后一分钟回复)。晚了的有 4 条:ask-27 3 小时 15 分、ask-21 5 小时 59 分、ask-32 7 小时 09 分、ask-37 7 小时 54 分。
  • 第 3 节的论点在 story 上不成立:手写的 Agent 换问题很勤,没等到回答就关上的问题反而比答掉的短。拨盘的“只有一两件”在 story 上也不成立,这里同样照实记下。

两套推导的一处不一致。 story 的 ask-08(hiring,“面谈前写好小团队的设计职责”)在 f10 又登记了一次,可那一帧条目已经撤下。live.mjs 只要有登记就算开着,把它延到 f11(10/15 21:00),关上时已过期限 19:30 一个半小时;askLife 在 f10(14:30)就关上,离期限还有 5 小时。两个数据集里只有这一处不同。动画用 askLife 的口径(登记时条目不在根树上的登记不算),与拨盘、脉冲一致;落到代码时要把 live.mjs 的这一处对齐,身份线与小倍数也读 askPaths,只影响 story 这一条。

3 故事 ​

论点:问题不会一直等你;开得最久的,往往是别人先开了口。 composite 两周里,开得最久的两个问题都不是你答掉的。派对回几个人开了 252 小时,过了期限一天,是 Kajsa 替全家报了人数。母亲九月住哪开了 240 小时、登记 14 次;你为这件事发了 6 条消息、打了 2 个电话,没有一次回答它;期限前 12 小时,母亲自己交了回执。答掉的 11 个问题中位只开了 36 小时,没等到回答就关上的 8 个中位开了 156 小时。

英雄数字:240(小时)。 英雄问题从登记到关上开了多久。它是一只往上数的钟,单位用小时:composite 每轮正好 12 小时,写成“10 天”就把一步一步的等待藏起来了。

  • 第 1 拍问题登记时出现,显示“已开 0 小时”,字高约为右栏宽度的七分之一,全场只有它这么大。
  • 此刻线每到一个批次刻度,它就硬切加 12,不滚动:数字只在节拍上动。
  • 旁边一行小字是另一只往下数的钟:“离期限 251 小时”。两只钟加起来不变。
  • 第 3 拍问题失效时它停在 240,标签从“已开”换成“开了 240 小时 · 改写后失效”,“离期限”停在“还差 11 小时 59 分”。它不是被一道朱红刻停下的。
  • 图上同步回声:英雄格下方一条细墨括号从登记延到此刻线,末端写同一个数,格的长度就是这个数。

英雄问题怎样选。 规则是:两周里登记次数最多的问题,并列时取开得久的;只算登记时条目还在根树上的登记。帧号由规则算出,不写成常量。

  • composite 选出 ask-08“母亲九月住哪?”(14 次,唯一最多)。
  • story 选出 ask-26“苏禾改约周四 19:30”(2 次,与另外三条并列,开得最久,81 小时 30 分)。它的结局与 composite 同一类:10/22 18:34 苏禾来信说家里临时有事、改周六上午;f29(18:40)问题换成新问题,离期限 19:30 还有 50 分钟;12 分钟后你点了“稍后提醒”,第二天 09:30 到点,09:31 你回复。这正是概念卡给 story 选 f30 的那段“稍后提醒到点、你再回复”的回路。

三拍(composite 约 24 秒,括号里是 story;放一次就停,不循环)

拍时长与帧发生什么观众此刻主图 / 面板
1 登记0–4.0 秒,f00 → f03(story:cold-00 → f19,快进不超过 1.5 秒)0–0.4 秒静止,只有空的时间轴与泳道分组;0.4 秒此刻线从轴上落下(第一个动作小、贴着锚点);0.7–2.2 秒此刻线走过 36 小时,f01 一次登记 5 个问题,f02 一个,f03 两个;f03 后 0.4 秒游标落到“母亲九月住哪?”,0.3 秒后期限倒三角落在 8/14 23:59,一条墨色虚线从此刻线连到它问了我一件事,有期限格从此刻线长出;其余泳道降到七成 / 右栏切到“这一件”:“已开 0 小时 · 离期限 251 小时”
2 等4.0–13.5 秒,f03 → f22,19 轮、每轮 0.5 秒(story:f19 → f29 前,约 4.5 秒)每轮此刻线用 0.38 秒缓出走完 12 小时,在批次刻度上停 0.12 秒做硬切(f04–f21 共 18 次):英雄数字加 12,离期限减 12;其中 12 次是重新登记,各在格上压一道刻痕;你的 8 条反馈在此刻线经过它们的时刻落成朱红短刻,没有一条关上它;周围的格开开关关,答掉的落下朱红长刻;8/12 23:59 派对过了期限,斜线跟着此刻线往右长。最后一轮滑到 f22 为止,f22 的硬切归第 3 拍时间在走,它一直开着;我动过,但没回答它英雄泳道不闪不跳,只变长 / 面板随刻度加行:登记方块、你的动作句
3 别人先开口13.5–24 秒,f22 → f23,再落到 f28(story:f29,落到 f34)f22 硬切:英雄格第 14 次登记,已开 228 小时、离期限 23 小时;派对随事项撤下,末端竖线,过期斜线 24 小时。此刻线走向 f23,路过 08:02 与 11:38 时,猫看病与待审 PR 两行落下朱红长刻(给诊所打电话、批准 #13572,你答掉的两件)。f23 硬切:其余泳道压暗到 0.26,0.3 秒后英雄格换成虚线、英雄数字停在 240,标签改写为“Mamma已交回执,选疏散公寓”,10:50 与 11:05 两个墨点落在格尾之前(关上它的声明所引的来源);静 1.2 秒。随后格沿自身向上弹出 8 px,引线出到一张放大卡:8/14 09:00–15:00(关上前后各 3 小时)的小时间轴,10:50 Ulrika、11:05 母亲、12:00 整理(问题失效),小此刻线走到 12:30,落下一道朱红短刻“你用短信回复母亲”,这是全场唯一的亮点;静 1.5 秒。卡收起,此刻线用 1.5 秒走到 f28:派对与母亲两行各长出一个新格,0.4 秒后游标甩到右栏第一件“主水管:挖开换还是免挖?· 期限 8/17 10:00 · 还有 10 小时”它不是被我关上的;我回的时候问题已经关了;现在最该回的是这一件旁白依次是 frames[22].title、frames[23].title“打了诊所;母亲自己定了”与 frames[28].title“两周结束:水管还没回” / 面板写关法与声明理由,落定后切回“此刻仍开着 7 个问题”,第一件带“去回答 →”

两层

  • 信息层(九成以上,少、准、有出处,只在节拍上动): 时间轴、泳道、问题格、刻痕、朱红刻度、期限、墨点、此刻线、游标、英雄数字与面板。每个数都能在第 2 节对上出处,页面用同一推导现算。
  • 概念层(一成以内,只用灰): 批次条里一粒一条输入的灰点,按时刻落下,到刻度时扫进那一轮的柱子,英雄问题开着的 240 小时里落下 2342 粒;还有此刻线的拖尾。
  • 共用几何与数量: 两层用同一条真实时间轴,灰点落在它那条输入的 event.at 上,柱高等于灰点数。
  • 不打架: 概念层只在批次条与此刻线上,不进泳道、标签栏与右栏;最亮不超过已关上的格;此刻线停下它就停;空轮(story 的 f17、f27)不落灰点。

动起来之前的约束

  • 绝不动: 时间轴的范围与刻度(一个镜头到底,不推拉;要看清分钟,只在卡里放大);泳道顺序(按流分组,组内按首次登记);朱红的含义。
  • 此刻线只向前走,不过冲。 用户按 ← 或选帧往回时,离散量一帧回到那一轮,此刻线缓动回去,不倒放事件。
  • 每轮只硬切一次: 登记、重新登记、关上、改名、计数都在批次刻度上;你的反馈与期限在此刻线经过它们的时刻出现,面板同一帧加行。
  • 只给关上的那一刻画墨点。 墨点只画关上它的那条声明 sources 里的输入,此前图上从不画别人的输入;派对那件的撤下声明没有 sources,所以不画点,理由写在面板里。
  • 只读同一份回放: frames、批次、asks、feedback,不加只为动画服务的字段,也不改契约。
  • 什么时候放: 直接打开(顶层、不带 ?frame=)时放一遍故事;任意键或点击跳到终态,顶栏“▶ 讲一遍”重放;带 ?frame=、嵌在合页或画廊里、从别的页链过来时停在那一帧不放;开了 prefers-reduced-motion 时直接到终态。之后 ▶ 连播按同一节拍:每 12 小时 1 秒,批次刻度上停 0.12 秒;←/→ 逐帧用 0.38 秒。

交互

与份额拨盘、整理脉冲同一套标准,时长与卡片样式取共用的 motion/ix.mjs(另一个代理正在抽取),这里只写回应时序几何上的对应。悬停与展开都让故事或连播停在当前时刻,概念层随之静止。

  • 悬停
    • 触发:鼠标移到问题格、泳道名、此刻线上的号码圈、右栏的行或登记方块上(命中区放宽到格上下各 4 px);Tab 到泳道名;触屏轻点一下就是悬停。

    • 一条时序线出一张概要卡(纸底、墨框、等宽字),卡上的数守当前这一轮的节拍。f22 时悬停母亲那条,卡上写:

      text
      选择 · 母亲九月住哪?
      家庭 · Mamma回执今天截止,昨晚已通话
      开着 · 已开 228 小时(19 轮)· 登记 14 次(本轮重新登记)
      期限 8/14 23:59 · 还有 23 小时
      你在这件事上动了 8 次,回答它 0 次
    • 这条泳道、它的期限、刻度与右栏那一行保持原样,其余泳道降到七成,概念层降到四成。

    • 悬停朱红刻度:卡上写动作句、时刻,以及答了还是没答登记的问题,如“你给母亲打了电话 · 8/13 19:30 · 没有回答登记的问题”。

    • 收回:离开 0.15 秒后,卡用 0.12 秒淡出,再过 0.4 秒接着走。

  • 展开
    • 触发:点问题格、泳道名、号码圈、右栏的行或概要卡;Tab 到泳道名后按 Enter 或空格。
    • 0 秒是切点:页头改写为“这一件 · f23 · 8/14 12:00 / 母亲九月住哪?”,右栏切到“这一件”。
    • 0–0.3 秒,格连同它的刻痕、刻度与墨点向上弹出 8 px(垂直于时间轴,缓出,过冲 3.7%)。泳道的中线与时间位置不动。
    • 0.4–0.6 秒,引线从格的右端(关上的点,开着就从此刻线)沿泳道水平出去,再转向卡片。
    • 0.6–0.9 秒,卡片从引线末端展开,先竖后横。卡放在此刻线右侧的浅底里;那里放不下(晚的帧几乎没有浅底)就放在泳道上方或下方离此刻线远的一侧,避开页头与右栏。
    • 卡上:问题全文与选项(推荐项标出);一条放大的时间轴,从登记到关上(或此刻),关上前后各 3 小时再放大一行,看得见 30 分钟的先后;你的回应(动作句,答了或没答);怎样关上(关法、声明理由、所引来源);链接见下。不承载作答。
    • 收回:Esc、点空白处、再点同一格。卡 0.2 秒收起,引线 0.15 秒缩回,格 0.2 秒落回,然后一帧切回页头与右栏,0.4 秒后接着走。点另一格先收回再展开。←/→、选帧与合页顶栏选帧会立即收起并照常执行。
  • 面板“这一件”(展开时与故事里的右栏)
    • 流 · 事项短名 · 期待(选择 / 确认 / 批准……)。
    • 问题的短问法与全文。
    • 英雄大字“已开 N 小时”或“开了 N 小时”,下面一行“M 轮 · 登记 K 次 · 离期限……”。
    • 登记方块:开着的每一轮一格,登记过的那几轮实心。
    • 你的回应:动作句列表,关上之后的动作另起一段写“关上之后”。
    • 怎样关上:关法、reason 与来源(只写渠道、人与时刻,不贴原文)。
    • 最后一行是链接,再下面是“全部记录 →”,打开现有的详情抽屉(原文、版本、JSON)。
  • “去回答 →”链到哪一帧
    • 问题在当前帧开着:链到同一帧的 D 手机首页,../../index.html?variant=d&frame=<当前帧序号>,经 withDataset 带上 ?data=,与拨盘、脉冲相同。故事落定时是 composite 第 28 帧的主水管,story 是第 36 帧(f34)的“周一谈合作前,准备暂存列这个取舍”,恰好又在苏禾那一行。
    • 问题已关上:不给链接,卡与面板写它怎样关上;同一件事此刻另有一个开着的问题时点出它在右栏的号码,如“这件事此刻另有一个开着的问题:母亲 23 日来吃饭?(右栏 7 号)”,链接在那一行上。
    • D 只把最急的 3 张做成大卡,其余折成计数;从这里链过去的问题可能在折叠里,D 目前没有按问题定位的参数。

已定(审阅,2026-10-19)

  1. 动画落在哪里: 原地升级 sketches/timing.html。直接打开放一遍故事,之后 ▶ 连播与 ←/→ 用新节拍。
  2. 故事落定在哪一帧: 最后一帧(composite f28 / story f34)。页面不带帧打开、嵌入时的默认帧仍是数据里的 f26 / f30,不跟着改。
  3. 期限的口径: 按每次登记画期限,被改掉的旧期限变灰;“Smilla 多吃了药”因此是晚 2 分钟答掉。

4 motion:怎样做的 ​

  • 一只表,两种量。 整页一个 requestAnimationFrame 循环。播放器读一条“轨”:每段写明起止秒、此刻线从哪个时刻走到哪个时刻、缓动,算出连续的此刻 t 与离散的帧 k。t 越过批次刻度时 k 才变,于是格、刻痕、关法、右栏与页头都在刻度上一次硬切;你的反馈、期限过期的斜线只看 t,在此刻线经过时出现。往回走时 k 立即回到那一轮,此刻线再缓动回去,不倒放。两周的图仍只建一次,换帧只切类名(中位 1.1–1.5 毫秒)。
  • 故事是由数据算出的一条轨。 英雄问题照规则现选;拍点是相对英雄登记、关上与最后一帧的偏移:0.4 秒此刻线落下,0.7–2.2 秒走到登记,2.6 秒游标落定、右栏切到“这一件”,2.9 秒期限落下;第 2 拍每轮 0.38 秒缓出加 0.12 秒停;第 3 拍先停 0.5 秒,1.2 秒走到关上的那一轮(硬切比压暗晚 0.3 秒),静 1.2 秒,放大卡展开,卡里的小此刻线走 1.2 秒,静 1.5 秒,卡收起,1.5 秒走到最后一帧,0.4 秒后游标甩到右栏第一件。composite 讲完约 23.5 秒,story 约 17 秒。
  • ▶ 连播与 ←/→ 用同一节拍。 连播每轮按跨越的时长走(12 小时 0.88 秒,夹在 0.1–2 秒之间)再停 0.12 秒;←/→ 一步 0.38 秒,跨多帧最多 0.9 秒。合页顶栏选帧经 followFrames 进来,也走这段过渡,不硬跳。
  • 期限按每次登记。 每次登记取那一刻条目上的期限,压成一串台阶;新的一级出现时旧的变灰虚线,过期斜线按各自那一级画。
  • 游标墨黑。 竖条加圆圈,换泳道时从上方 12 px 落下,0.3 秒、过冲 3.7%(与 IX 的弹出同一缓动)。
  • 概念层只在批次条与此刻线后。 一张画布叠在批次条那一带:每条输入一粒灰点,在此刻线经过它的 event.at 时用 0.22 秒落下,刻度上 0.12 秒扫进那一轮的柱子;此刻线后一条 6 小时宽、不透明度 0.1 的浅灰拖尾,停下 0.6 秒内淡掉。卡展开时它跟着压暗六成;减少动态时整层不画。
  • 交互接 motion/ix.mjs,不另起一套。 宿主只给几何:一条时序线的命中带是泳道中线上下各半格加 4 px,右端多 12 px;泳道名、号码圈、刻度与右栏的行也指向同一条路径。概要卡贴着指针、避开这条泳道;详情卡放在此刻线右侧(至少 330 px 宽的浅底),放不下就放在泳道上下离此刻线远的一侧,引线从格的右端出发。卡里的内容由本页写(peekHTML / detailHTML,与批次记谱同一钩子)。
  • ix.mjs 只加了一个口子:ix.present(id)。 第 3 拍的放大卡要用同一张详情卡、同样的展开与收回,却不能停住故事自己的表。present 与点击展开走同一段代码,只是记下 presented,不停宿主的表;悬停照样停表,人点别的一件就照常接手。拨盘、脉冲、腕上一瞥与批次记谱不调用它,行为不变。
  • live.mjs 的两处。 登记只在条目于那一帧仍在根树上时才算(与 stage.mjs 的 askLife 同一口径):composite 的路径一条不变;story 的 ask-08 少算了撤下那一帧的一次登记,提前一帧(f10)关上,身份线 story 从 f10 起少画一小段问题线(约 40 像素),小倍数不变。mountLivePage 加了 tools(顶栏多放一个按钮)与 onPlay(▶ 交给本页的播放器),合页跟帧时带上 from: 'embed';其他页面不传这两项,行为不变。variants/shared.mjs 加了 familyPlace,把家庭流的渠道写成“Ulrika(兄妹群)”。
  • 钩子。 window.foldTiming 与 foldDial 同名同义:player、hold、startPlay、startHero、skip、seek、perf,加上 ix.hooks() 的 hover / open / close / ixAt…,以及给检查用的 pick()、geom(name)。?at=秒 把故事冻在那一秒,供截静帧。

5 direct:看过之后改的 ​

  • 放大卡的窗口放宽到关上前后各 3 小时(09:00–15:00)。 10:00–13:00 装不下 10:50、11:05 两个墨点的标签与 12:30 那道朱红刻;交互一节本来就写“关上前后各 3 小时”,两处统一成一条规则,每条路径的卡都这样放大。
  • 关上那一轮先压暗、再换形。 压暗与虚线、240、改名同时来时,眼睛还没落到英雄泳道就已经变完;硬切推迟 0.3 秒。
  • 第 3 拍其余的朱红退成灰。 故事说放大卡里那道刻是全场唯一的亮点,但到这一轮图上已有几十道你的回应刻度。从压暗开始到卡收起,图上的回应刻度降到 0.18,右栏与卡里此前的动作句改成灰;卡收起后一起回来。
  • **右栏在游标落定时切到“这一件”,**而不是登记那一刻;切早了,眼睛还在找刚长出来的格。
  • 悬停照 ix 的标准停住故事;点空白或任意键跳到终态。 没做“重播这一件 ▶”与“看关上那一轮 →”:前者要第二条故事轨,后者与 ← 重复;关上的问题按审阅只说怎样关上。
  • 右栏的行不再是按钮。 ix 不接管按钮上的点击,行改成带 data-id 的块,点它与点格一样展开;面板“这一件”本身不带 data-id,悬停它不会停住故事。
  • story 的英雄是 ask-26。 讲约 17 秒,落在 f34,游标停在“周一谈合作前,准备暂存列这个取舍”。右缘的期限标签在这一帧会与相邻格的边线相压(此前就有,见打磨记录 A 的“未解决”),没有改。

6 ship:验证 ​

  • make check 通过。
  • node scripts/check-ix.mjs:回应时序 16 / 16 项,composite 与 story 都过;ix.mjs 加了 present 之后,整理脉冲、份额拨盘、腕上一瞥与批次记谱两套数据也都还是 16 / 16。
  • node scripts/shoot-forms.mjs --only timing,lineage,all --gpu --check all-sync:两套数据都过,合页里回应时序跟上顶栏选的帧、不讲故事。用不带监听队列的 python -m http.server 起服务时,合页一次开二十多个 iframe 会有模块请求被重置(报在让位图或整理脉冲上),换回 prototype/serve.py 同样的 256 队列就没有了,与本页无关。
  • 帧率(无头 Chrome 开显卡,1440×900):composite 讲故事全程 59.6 fps,每帧 p95 16.8 毫秒,本页每帧的活 1.8 毫秒(p95 2.6);▶ 连播 57.4 fps,←/→ 逐帧 59.7 fps。story 三项都是 60 fps。换帧切类名中位 1.1–1.5 毫秒,建图一次 63–68 毫秒。
  • 讲一遍实测:composite 23.4 秒落到 f28,story 17.3 秒落到 f34。任意键与点空白都跳到终态,“▶ 讲一遍”从头再讲;?frame=、从画廊链过来、嵌入合页都停在帧上不讲;减少动态时直接到最后一帧。
  • npm run site:build 通过;画廊卡片预览 site/previews/timing.webp 按 工作环境说明 重截(默认帧 f26)。
  • 没在 Safari 里看过这一版。