切换外观
批次记谱 · 动画:参考、数据、故事与实现
状态:已实现(2026-10-20,分支 codex/motion-notation)。前三步(参考、数据、故事)审阅通过后照做,第四到六步(motion、direct、ship)的取舍与验证记在文末第 4–6 节;审阅定下的三处(原文末“待定”)照审阅改了正文。
2026-10-19。第三轮立下了一套标准:六步流程、两层法则、交互统一视觉标准,见 CONCEPTS.md、整理脉冲 story、份额拨盘 story。这里把它用到 05 批次记谱 上。第 1–3 节是参考、数据、故事,第 4–6 节是实现、看过之后的修改与验证。动画直接做在现有页面上(notation.mjs、stage.mjs),不另开 motion/ 页,也不算官网首屏动画。页面仍只读同一份 scenario.mjs 回放,不改数据,不改契约。现在的画法与交互见 review-b.md。
1 参考
这张谱现在怎么讲比例
本机按 1440×900 截了 composite ?frame=28 和 ?data=story 默认帧,看过,不提交。
能讲清楚的:
- 一轮一行,竖着读能看出两周的节奏。收到条长短参差(composite 每轮 54–234 条);改动记号在 f01、f16、f18、f22 四处扎堆;◆ 散落在 18 轮里。
- 单看一行,留下几成读得出来:收到条按处置分三段,黑段是采用。
- 悬停一个改动记号,这件事在每一轮的记号会一起亮起,能追出来龙去脉。
讲不清楚的:
- 两种单位并排,比例画反了。 收到条每条 0.73 px(最长的 234 条占 170 px),改动则一件画一个约 10 px 宽的记号。f18 的 15 个记号排开约 170 px,比它 151 条的收到条(约 110 px)还长,画面看起来像“动的和读的一样多”。story 下更明显:3 条输入的条约 57 px,旁边常排着 3–5 个记号。
- 没有合计。 两周收到 3196 条、采用 1177 条、动了 131 处,这三个数页面上哪里都没有,侧栏也只有“这一轮”。“读了很多、丢了大半、动了几件”只能靠读者逐行心算。
- 丢掉的部分画得最轻。 噪音段用最浅的线色,暂缓段用浅灰,整根条看上去以墨色为主。占六成三的那部分,反而是画面里最弱的。
- 漏斗两端不挨着。 收到列和改动列之间隔着问题列;间隔列在 composite 的 28 行里画的都是同一条 12 小时短线。
- 默认帧截掉了结尾。 composite 默认停在 f22,f23–f28 六行藏着,一打开看不到两周全貌(见第 2 节)。
- 现有动画只是新行落下。 新行落下时,收到条用 260 ms 长出来,记号晚 120 ms 淡入,旁白最后出现。记号不是从收到条里生出来的,“从多到少”这个过程没有演出来。
从整理脉冲借什么、不借什么
批次记谱讲的正是脉冲面板上那组数:收到、没采用、动了、新问。不同在于,它把 28 轮同时排成一张谱。脉冲一次只演一轮,“进来的大起大落、动的几乎不变”要连看两分钟才体会得到;谱上 28 行并排,一眼就能比。所以动画只做谱能做、脉冲做不到的事:让这个比例在行与行之间读出来。
| 要素 | 借过来 | 不借,或改掉 |
|---|---|---|
| 一颗一条 | 一颗颗粒就是一条输入,可以逐行核对。这是脉冲第三轮按用户要求定下的:画面上的数量要和真实事件数一致 | 尘埃、辉光、余辉、WebGL;纸底不需要发光 |
| 各类输入的去向 | 噪音与重复熄灭,暂缓化成灰烬,采用的被带走,未判的原地散掉 | 收拢弧和径向几何;谱的几何是行与列 |
| 读数的次序 | 收到 → 没采用(分项落定时再补上)→ 动了 → 问题。每个数只在自己那一拍出现;“动了”只算新增、变大、撤下、合并 | 四格流水线面板。谱的列头本身就是流水线,每到一拍由灰转墨 |
| 节拍手法 | 第一个动作在 0.4 秒,要小,贴着锚点;离散量一帧硬切,连续量用 ease-out,带约 3.7% 过冲;高峰后留一段静场;悬停时钟停住,卡上的数守节拍 | 一轮 4.25 秒的循环和 124 秒连播。谱的三拍同时作用在所有行上,只演一次 |
| 字号 | 全场有一个最大的数 | 脉冲的英雄是本轮收到 N,“动了”只有它三分之一大。谱反过来:两周的“动了”最大,“收到”最小 |
| 第三拍 | — | 脉冲第三拍“一问”整屏压暗,朱红线是唯一的亮点。谱上朱红只表示来自你本人;问题列在第三拍末尾一次硬切出来,是回声,不是高潮 |
| 主题 | 份额拨盘的纸底、墨框、等宽字;交互卡与拨盘同一套 | 脉冲的近黑底 |
概念层占比:约两成
第三轮两个动画是八成概念层、两成信息层。批次记谱反过来:概念层约占两成,包括收到列里的颗粒、行下的灰烬、第三拍流向改动列的流痕,第三拍时短暂到三成五;信息层约占八成。理由:
- 它是复盘工具。 读者会停几分钟,逐行读旁白、悬停记号、固定一件事。8 列 × 29 行再加侧栏,信息层不能让出画面。官网首屏只有 4–20 秒,要第一眼就成立;谱要第二眼、第三眼还读得下去。
- 这里的概念层不是装饰。 一颗一条,颗粒本身就是“读了多少”的质感;比例靠颗粒的多少和记号的多少一起讲出来。再加字形碎片或仪表,只会冲淡它。
- 静止时完全不动。 概念层只在开场三拍和切帧那一行里动;平时唯一的变化是悬停时周围变淡,借参考视频 A 的“焦点最慢”。
- 两层仍共用几何和数量。 颗粒铺在收到条的同一把比例尺上,条长 = 颗粒数 × 单位;流痕沿本行走到它所进那件事的记号上。
2 数据
数字由本机临时脚本重算,推导与页面相同:行的算法取自 notation.mjs,问题的开闭取自 stage.mjs 的 askLife。脚本放在 scratchpad,不进仓库。动画页运行时用同样的推导现算,不读本文。
这张谱用的是哪套数据、哪一帧
| 情况 | 数据集 | 帧 | 出处 |
|---|---|---|---|
| 默认打开 | composite(Jonas Berglund,29 帧,f00–f28) | f22:8/14 00:00,热浪第一天(sketchNotes 写的理由是“这一轮的声明最密”);f23–f28 隐藏 | scenario.sketchFrames.notation = 'f22' 与 sketchNotes.notation,经 kit.mjs 的 chosenFrame 取出 |
?data=story | story(林苒,37 帧:cold-00、cold-01、cold-02、f01–f34) | f34,最后一帧,序号 36 | catalog.mjs 的 sketches 里那条 frame: 'f34' 是专门给 story 写的默认帧(注释写着 story default)。页面实际读的是 notation.mjs 里的 storyIndex: 36;画廊卡片上的“默认帧”优先显示 sketchFrames |
?frame= | 两者都可 | 指定的序号或帧 id | chosenFrame |
CONCEPTS 第二轮的概念卡和 ASCII 草图画的是 story 的 f34(37 轮,开局一轮 9 条);review-b 的打磨与截图用的是 composite 的 f22、f10、f28。下面两套都算,动画以 composite 为主。
推导
- 收到:
frames[i].events.length。不用signal.received,因为 composite 去掉了 21 封营销邮件,个别帧会差 1。 - 采用:
event.disposition === 'used'。没采用 = 收到 − 采用 = 暂缓 + 噪音 + 重复 + 未判。 - 动了:取
records[i].diff.changes,去掉annotations_changed,按 notation.mjs 的规则归类,数新增、变大(含 restored)、撤下、合并的件数,子项也算。变小与只改内容另记为“安静的改动”。一处 = 一件事在一轮里动了一次。 - 问题:取自
askLife[i],分四种:fresh 新登记;answered 你答了(问题关上了,而且此前有feedback[].ask指向它);lapsed 失效(关上了,但没人答);open 此刻开着。 - 来自你:
event.self,或from === persona.name。回应数event.feedback[],批注数frame.annotations。 - 采用的输入进了哪件事:先找本轮批次声明里引用它的那条(
sources[].ref = 'event:<id>'),找不到就用relatedIds[0]。确定这件事后,先看它本身、再沿父项与合并找到它的根(同 pulse.mjs 的rootAt),看这一轮有没有被“动了”。
两周合计
| 量 | composite | story | 出处 |
|---|---|---|---|
| 轮数 | 28,轮轮有输入,没有定时整理 | 36:34 轮有输入,f17、f27 是 0 条输入的定时整理 | frames 里 phase 不是 initial 的帧;signal.type === 'review' |
| 收到 | 3196 条 | 112 条 | Σ frames[1..].events.length |
| 采用 | 1177 条(36.8%) | 84 条(75.0%) | Σ disposition === 'used' |
| 没采用 | 2019 条(63.2%)= 暂缓 1117 + 噪音 864 + 重复 33 + 未判 5 | 28 条(25.0%)= 噪音 12 + 重复 10 + 暂缓 6 | Σ disposition |
| 动了 | 131 处,落在 60 件事上 = 新增 60 + 变大 23 + 撤下 25 + 合并 23;全在根层 | 69 处(根层 41),落在 27 件事上 = 新增 27 + 变大 35 + 撤下 5 + 合并 2 | Σ records[i].diff.changes,按上面的规则归类 |
| 安静的改动 | 156 处 = 变小 48 + 只改内容 108 | 77 处 = 变小 44 + 只改内容 33 | 同上 |
| 比例 | 每 100 条收到 → 37 条采用 → 4.1 处改动;每 100 条采用对应 11 处 | 每 100 条 → 75 条 → 62 处 | 上面三行相除 |
| 新登记 / 你答了 / 失效 / 最后仍开着 | 26 / 11 / 8 / 7 | 39 / 11 / 26 / 2 | Σ askLife[i] 各类的个数;最后一帧的 open.size |
| 来自你 | 583 条(18.2%);回应 160 条,其中 32 条答的是登记过的问题,全部 via: source;批注 0 | 26 条(23.2%);回应 15 条,其中 12 条答问题(reply 12、button 3);批注 3 条(f03、f14、f33) | self;feedback[];annotations |
按流看 composite 的采用率(event.stream):工作 768/1676(45.8%),家庭 238/388(61.3%),生活 171/326(52.5%);社区 738 条、订阅 68 条,一条都没采用。按周看:第一周(f01–f13)收到 1411、采用 552、动了 53;第二周(f14–f28)1785 / 625 / 78。
每轮的形状(composite)
| 量 | 最小 | 四分位 | 中位 | 最大 |
|---|---|---|---|---|
| 收到 | 54(f11、f25) | 75 – 152 | 108.5 | 234(f20) |
| 采用 | 7(f11、f27) | 33 – 55 | 38 | 94(f08) |
| 没采用占比 | 23.9%(f15) | 50% – 72% | 63.1% | 89.4%(f27) |
| 动了 | 0(f27) | 2 – 6 | 3 | 15(f18) |
| 新登记 | 0(共 10 轮) | 0 – 1 | 1 | 5(f01) |
- 丢掉的跟着收到走。 每轮收到的条数与没采用的条数相关系数 0.91;没采用的占比却几乎不随收到变化(0.17)。28 轮里有 21 轮丢掉一半以上。
- 动了的不跟着走。 收到与动了的相关系数只有 0.43。收得最多的 f20(234 条)只动了 3 处;动得最多的 f18(15 处)收到 151 条,在 28 轮里只排第 8。
- story 正好相反。 收到与动了的相关系数是 0.79;收得最多的 f01(9 条)也是动得最多的一轮(16 处,其中 10 处是子项)。
极值轮
| 问 | composite | story |
|---|---|---|
| 收到最多 | f20(8/13 00:00):234 条,采用 66(28.2%),动了 3 处。frames[20].events.length;signal.received 是 235 | f01(10/12 09:10,序号 3):9 条,采用 8,动了 16 处 |
| 采用率最低 | f27(8/16 12:00):66 条只采用 7(10.6%),动了 0 处;这 7 条全进了只改内容的两件(主水管、master CI) | f06(10/13 18:20):3 条采用 1(33.3%),回执是“检查”(r7) |
| 采用率最高(作对照) | f15(8/10 12:00):71 条采用 54(76.1%) | 有 7 轮是 100%(cold-02、f07、f11、f12、f18、f19、f23) |
| 动得最多 | f18(8/12 00:00):15 处 = 新增 3 + 变大 2(母亲换水管 12→24 份、猫看病 3→12 份)+ 撤下 6 + 合并 4 | f01:16 处 |
| 登记问题最多 | f01:5 个,因为是从空状态起步;之后每轮最多 2 个(f03、f22、f26、f28) | f01:6 个 |
| 失效最多 | f22(8/14 00:00):3 个,“派对回几个人?”“Signe 的同意书签了吗?”“要 Göran 来抬棚屋吗?”。其中同意书一问是旧条目并进了新条目,同一轮又以新 id 重新登记 | f24:4 个(ask-28 至 ask-31)。这一轮旁白是“暂存列已隐藏,本月 API 预算定为 5200 元”,被改写的四件事都没有重新登记问题 |
| 你答得最多 | f05、f16、f23,各 2 个 | f05:2 个 |
采用的输入去了哪里
- composite: 1177 条采用里,541 条(46%)进了本轮“动了”的那几件,599 条进了只变小或只改内容的事,37 条进了本轮没变的事;每一条都有去处。131 处改动里,91 处有本轮输入推动,40 处没有:23 处合并全在其中(输入记在并入的目标上),另有撤下 14 处、新增 2 处、变大 1 处。也就是说,新增和变大几乎都是本轮输入推动的(83 处里 80 处);撤下与合并大多没有输入推动,正是因为没有新输入。
- story: 84 条采用 → 进了动了的事 42 条,进了安静改动的事 37 条,进了没变的事 5 条。69 处改动里 36 处有输入推动,33 处没有,多数是子项:输入记在父项上,子项随父项一起新增。
- f20 逐颗核对: 234 颗 = 采用 66 + 暂缓 111 + 噪音 57。66 颗采用里,11 颗流到 3 个记号上,55 颗流进 ◇8 ▼1。
3 故事
论点。 读得越多,丢得越多;动的却不跟着变多。两周收到 3196 条,丢了六成三,状态只动了 131 处。收得最多的一轮(f20,234 条)只动了 3 处,动得最多的一轮(f18,15 处)收到的条数只排第 8。
英雄数字:131。 这是 composite 的值,story 现算为 69。两周里状态被动了 131 处:新增 60、变大 23、撤下 25、合并 23,落在 60 件事上。它是“读了很多、丢了大半、动了几件”里最后、也最要紧的一项。账上三个数字号与数值倒着排:3196 字最小,131 字最大,这处倒置本身就是论点。131 在第三拍出现在旁白列里(开场时这一列空着),字高约为谱高的六分之一(约 110 px),随记号落定从 0 计到 131;收束时它落进谱底合计行的改动格,之后不再变大。不写成“4%”(131 ÷ 3196):改动和输入不是同一种单位,动的多少也不随收到的多少涨落,写成百分比会让人误以为“每 24 条输入换来一处改动”。
三拍(开场共 12 秒。0–0.4 秒静止,谱的骨架已经在:列头、时刻列、日分隔线、合计行的空框,还没有颗粒和数字。第一个动作在 0.4 秒:列头“收到”由灰转墨,第一行行首落下第一颗颗粒)
| 拍 | 时间 | 谱 | 账与列头 | 观众此刻 |
|---|---|---|---|---|
| 1 读 | 0.4–4.0 秒 | 28 行从上往下写出:每行比上一行晚 0.11 秒起笔,颗粒用 0.45 秒从行首向右铺开,先快后慢,一颗一条,铺满的长度就是条数。这时颗粒都是同一种浅灰,判断还没出来。一行铺满的那一帧,行尾条数硬切出来,上沿的朱红(你自己的输入)同时出现;顶栏的帧刻度跟着一格格点亮 | 账上“收到”随颗粒落下逐颗计数,0 → 3196;列头“收到”是墨色 | 好多,而且每轮多少不一样 |
| 2 丢 | 4.0–6.6 秒 | 先静 0.3 秒。4.3 秒,所有行在同一刻动作:噪音、重复、未判的颗粒原地熄灭(0.3 秒);暂缓的下沉 3 px,成为行下沿一道灰烬(0.5 秒);剩下的采用颗粒向行首收拢、补上空位(0.5 秒,ease-out,过冲 3.7%),由灰转墨,交给信息层。收到条只剩外框还画到原来的长度。轮廓形状不变,墨色缩到原来的三分之一出头 | 5.3 秒,“采用 1177”硬切出来,同时出“没采用 2019 · 六成三”;5.6 秒补上分项:暂缓 1117 · 噪音 864 · 重复 33 · 未判 5 | 大半没了,而且每轮都丢掉差不多这么多 |
| 3 动 | 6.6–10.4 秒 | 列头“改动”转墨。每颗采用的颗粒放出一道流痕,沿本行向右跑到改动列:行与行之间从上往下错开 0.06 秒,每道 0.6 秒,跑过后 0.8 秒褪去;颗粒本身留在收到条里。进了“动了”那件事的流痕汇向它的记号(+▲⊘⋈),最后一道到达的那一帧记号硬切出来,带 3.7% 的放大过冲;进了只变小或只改内容那件事的,流进 ▼/◇ 小计;进了本轮没变的事的,在改动列前散掉。8.9 秒,没有输入推动的记号(composite 40 个)自己落下:先空心,0.2 秒后填实。9.4 秒列头“问题”转墨,问题列的 ◆◇↩ 在全部行里一次硬切出来。9.7–10.4 秒,两条细线标注各画 0.3 秒:f20 行尾写“收得最多 234 条 → 3 处”,f18 行尾写“动得最多 15 处 ← 151 条” | “动了”随记号落定逐个计数,0 → 131,是全场最大的字;9.2 秒补上分项:+60 ▲23 ⊘25 ⋈23 | 读了这么多,只动了这几处;而且动得最多的那一轮,不是收得最多的那一轮 |
收束(10.4–12.0 秒)。先静 1.0 秒。11.4 秒起用 0.6 秒(ease-out)把账上三个数竖着落进谱底合计行,各归各列;同时旁白从上往下逐行淡入,两条细线标注淡出。合计行左侧画一道大括号(借 du Bois 的合计条,灵感 #58,0.4 秒画完),把 28 行收拢到合计条上(实现时改成收到列下的一道横括号,见第 5 节)。12.0 秒,此刻线落到最后一帧,侧栏一帧切到这一轮,进入可交互状态。每拍落定的那一帧,谱上的颗粒或记号、列头、账上的数同时变:一件事说三遍(参考视频 B)。
合计行。 谱底的终止线,左栏写“两周”;此刻不在最后一帧时写“到此刻”,数也只累计到此刻。
- 收到格:一根满列宽的合计条,按比例画(比例尺与各行不同,旁边注明):采用 36.8% 实墨,暂缓 35.0% 画成行下灰烬,其余留空。条尾写 3196。
- 改动格:粗体 131,后面跟 +60 ▲23 ⊘25 ⋈23,安静的改动写作 ▼48 ◇108。
- 问题格:◆26 ◇8 ↩11。
- 回执格:r28。
- 旁白格:“读 3196 · 采用 1177 · 动了 131 处”。
静止时的收到条也随之改:采用部分画实墨,外框画到条数 n 的长度,暂缓部分画成行下的灰烬,噪音留空。被丢掉的部分直接显成空,比现在的浅灰段更醒目,补上第 1 节说的第 3 点。
列序。 改为:整理 | 间隔 | 收到 | 改动 | 追踪 | 问题 | 回执 | 旁白。收到挨着改动,从左往右读就是漏斗的方向。间隔列保留(story 的间隔各不相同),但不参与动画。
两层怎样叠
| 层 | 占画面 | 画什么 | 规矩 |
|---|---|---|---|
| 概念层 | 约两成;第三拍短暂到三成五;静止时只剩颗粒和灰烬 | 收到列里的颗粒(一颗一条)、行下的灰烬(暂缓)、第三拍沿行流向改动列的流痕 | 只用灰:颗粒在第二拍判定前是浅灰,采用的在判定那一刻转墨、交给信息层。不出字,不出数,不用朱红。最深处也比信息层的字和线浅;信息层每次动作的 300 毫秒里,概念层淡到六成。只在开场和切帧那一行里动,静止时不动。不进时刻列、旁白列、合计行的数字格和侧栏。颗粒数 = 条数 |
| 信息层 | 约八成 | 时刻与间隔、收到条(采用实墨 + 画到 n 的外框 + 条数)、上沿朱红、改动记号与小计、问题记号、回执、旁白、合计行、账、列头、此刻线、侧栏 | 墨黑加一个朱红。数字只在节拍上动,离散量一帧硬切。每个数都能在第 2 节找到出处 |
| 颜色 | 含义 |
|---|---|
| 墨黑 | 判定后的采用颗粒、记号、数字与文字 |
| 灰阶 | 判定前的颗粒、灰烬、流痕、外框、安静的改动 |
| 朱红 | 只表示来自你本人:收到条上沿的本人输入、批注 ✎、你答了 ↩。与本页图例一致;新登记的问题仍是墨黑 ◆ |
画法:颗粒、灰烬和流痕画在行下面的一块 Canvas 2D 上(composite 3196 颗),行、记号与文字仍用 DOM。颗粒尺寸:一行最多不超过 40 条时铺一层,否则叠三层;单位 = 列宽 ÷ 每层颗数,上限 8 px。composite 是 1.4 px 的方点,三层叠,间距 2.2 px,条长与现在相同(每条 0.73 px);story 是 6 px 的点,一层,间距 8 px。window.foldNotation.census(i) 返回第 i 行的颗粒数与各自去向,可以逐行核对,同脉冲的 foldPulse.census。
动起来之前的约束
- 绝不动: 行的顺序、位置和行高;各列的位置;时刻列;合计行的位置;记号的形状与含义(沿用现有图例)。
- 每行只动一次: 开场时演三拍,或者切帧到它时演一次缩短版。
- 颗粒数等于条数,去向与处置一一对应: f20 的 234 颗 = 采用 66 + 暂缓 111 + 噪音 57;66 颗采用里,11 颗的流痕落到 3 个记号上,55 颗流进 ◇8 ▼1。
- 不暗示数据里没有的事: 没有输入的轮不落颗粒。没有改动的轮(composite f27)没有流痕跑到记号上,7 颗采用的流痕全进 ◇2。没有输入推动的撤下与合并不画流痕,记号自己落下。
- 朱红只表示来自你本人: 新登记的问题是墨黑 ◆,不因为是“一问”就变红。这里与脉冲不同,照本页图例。
- 只读
scenario.mjs回放: 不另造数据,不改契约,不加字段。比例尺、颗粒、流痕全在渲染层完成。
切帧、重演与入口
- 开场 12 秒只在一种情况下演: 顶层窗口直接打开、地址不带
?frame=、没开减弱动效。演完落在最后一帧(composite f28、story f34),露出两周全貌。这就替换了 composite 现在的默认帧 f22;sketchFrames.notation这一页不再用,画廊卡片上的“默认帧”要跟着改。 - 其余情况直接到终态: 画廊缩略图、合页里的嵌入、带
?frame=的链接(包括“同一帧在其他形式”)、减弱动效。带?frame=时,那一行演一次缩短版。 - 缩短版(0.9 秒): →、↓ 或 ▶ 播放(每 1.5 秒一行)走到下一行时,0–0.3 秒颗粒铺开,行尾条数硬切;0.35 秒噪音熄灭、暂缓下沉(0.2 秒),采用的颗粒收拢;0.6–0.9 秒流痕跑到改动列,记号在流痕到达的那一帧硬切出来,问题与回执随后硬切,旁白在 0.9 秒淡入。合计行在 0.3、0.6、0.9 秒各硬切一次,跟上到此刻为止的累计。←、↑ 退一行时一帧硬切。
- 重演: 顶栏 ▶ 旁边加“重演”(键 R),从头演到此刻那一帧。
- 跳过与暂停: 开场中按任意键或点空白,直接跳到终态;悬停让开场停住。
- 此刻之后的行: 审阅定下仍然隐藏(只留时刻、淡到四成),与其他形式的“此刻”一致;默认落在最后一帧,直接打开就是两周全貌。
交互,与整理脉冲、份额拨盘用同一套标准,共用正在抽取的 motion/ix.mjs。卡用拨盘那一套:纸底、墨框、等宽字。悬停看概要,点击展开详情;两者都只读同一份回放。谱上的几何是行:一行自己的方向是向右,也就是流痕走的方向;一件事自己的几何是它贯穿各行的那条“声部”,也就是现在的追踪列。
- 悬停
- 触发: 一行(行首时刻、收到条、空白处、旁白)、一个改动记号、▼/◇ 小计、一个问题记号、合计行。记号的命中区放宽到字形四周各 3 px。触屏轻点一下就是悬停。键盘 Tab 到行。
- 一行的卡: 写“这一轮 · f20 · 8/13 周四 00:00”。收到 234 · 采用 66 · 没采用 168(暂缓 111 · 噪音 57)· 来自你 56 条;动了 3 处:+后刹 ⊘机票退款 ⊘基准测试,另有 ▼1 ◇8;问题:◆ 后刹排气定哪天? ↩ 机票退款回来了吗?· 此刻开着 6 个;对照两周中位数:收到 108.5、采用率 36.9%、动了 3 处。卡贴在行的上方,放不下就翻到下方,一根细引线从行尾的条数连到卡。
- 记号的卡: 写“这一件 · 后刹 · 新增 6 份”。本轮有几条输入进了它(按流分)、开着的问题与期限、两周里它动过几次。
- 问题记号的卡: 问法、所属事项、期限、状态(新登记;你答了,附动作句;失效,写明是改写后没重新登记,还是旧条目并入后以新 id 重新登记)。
- 合计行的卡: 两周的账与分项,以及收得最多、动得最多两行的对照。
- 画面怎么变: 被看的那一处保持原样;其余行、记号与文字 0.15 秒内降到七成,概念层降到四成。悬停一行时,这一行的颗粒保持原亮度。悬停一个记号时,这件事在所有行里的记号和追踪列的圆标保持(沿用现在的追踪);而且每一行里进了它的那几颗颗粒也保持原亮度,两周里哪些输入推动了它,一眼就能数出来。
- 守节拍: 开场中途悬停时,开场的时钟停住,卡上只写已经落定的事。第二拍落定前写“这一轮的判断还没出来”,第三拍的记号落定前写“这一轮的改动还没落定”。
- 收回: 离开后留 0.15 秒宽限,卡用 0.12 秒淡出;若在开场中途,再过 0.4 秒时钟接着走。
- 展开
- 触发: 点一行、一个记号、一个问题记号、合计行或概要卡;行聚焦时按 Enter;侧栏里的事项名可以 Tab 到,Enter 展开(沿用现在的键位)。
- 一行: 0 秒是切点,此刻线跳到这一行(
stage.go(i)),侧栏一帧切到“这一轮”。0–0.3 秒,整行沿自己的方向向右推出 8 px(ease-out,过冲 3.7%),上下两条行线加粗。0.4–0.6 秒,引线从行尾沿本行基线向右画到侧栏左缘。0.6–0.9 秒,侧栏内容从引线接上的那一点展开,先沿左缘上下展开,再向右。 - 一个记号: 0 秒切点,侧栏切到“这一件”,这件事被固定。0–0.3 秒,记号沿本行向右推出 8 px。0.4–0.6 秒,引线从记号向右走到追踪列,再沿追踪列上下经过这件事每一处记号所在的行(经过时那一行的圆标点亮),最后从此刻那一行出到侧栏。0.6–0.9 秒,侧栏展开,同上。
- 问题记号: 与记号相同;侧栏切到它所属那件事的“这一件”,把问题一节排在最前。
- 合计行: 整行向右推出 8 px,侧栏切到“两周”。
- 收回: Esc、点空白,或再点同一处:侧栏 0.2 秒收起、回到这一轮概览,引线 0.15 秒缩回,推出的行或记号 0.2 秒落回。点另一处时,先收回再展开那一处。←/→、↑/↓、播放、选帧和合页顶栏选帧都会立即收起,再照常执行。减弱动效时直接到终态。
- 侧栏
- 这一轮: 默认显示此刻那一轮;展开一行时,此刻就是那一行。顶部写“这一轮 · f20 · r20 · 窗口 12:00–00:00”,标题是
frame.title,接着是narrative。然后是新加的“这一轮的账”:收到 234 → 采用 66 → 其中 11 条进了动了的那几件 → 动了 3 处,旁边对照两周中位数。再往下:收到的处置分项与来源流;来自你(动作句,沿用foldSentences);按类列出的改动(事项名可固定);问题,列出此刻开着的全部问题,本轮新登记的标 ◆,每条带“去回答 →”,本轮失效的写明怎么失效,本轮答掉的写你的动作句;最后是回执。 - 这一件: 顶部写“这一件 · 短名 · 类别”,标题用
titleAt(id, i)。下面依次是:份额逐轮(28 轮的小条图,此刻那一轮墨色,之后的淡);每一轮的改动(沿用现在的列表,可跳帧);本轮的动作、份额前后、进了它的输入(按来源分组,标题截断);开着的问题与期限,带“去回答 →”;你的回应;摘要、依据、来源、版本(沿用itemDetailHTML)。 - 两周: 两周的账(3196 → 1177 → 131)与分项;按流的采用率;第一周与第二周对照;收得最多、动得最多两行,点击跳到那一轮;问题 ◆26 ◇8 ↩11,最后仍开着 7 个。
- 这一轮: 默认显示此刻那一轮;展开一行时,此刻就是那一行。顶部写“这一轮 · f20 · r20 · 窗口 12:00–00:00”,标题是
- 去回答 →
- 只给此刻还开着的问题。链接是
../../index.html?variant=d&frame=<i>,带上?data=,i 是此刻那一帧的序号。展开一行时此刻就是那一行,所以本轮新登记的 ◆ 一定能在 D 的同一帧里找到。 - 不链到此刻之后的帧。已答或已失效的问题不给链接,改写它是怎么关上的,例如“你在 GitHub 上批准了 #13572”“改写后没有重新登记”。
- 页面本身不承载作答,与脉冲、拨盘一致。
- 只给此刻还开着的问题。链接是
- 脚本钩子:
window.foldNotation提供replay / census和与foldPulse、foldDial同名同义的hover / unhover / open / close / ixAt / paused / pointOf,事项名可用条目 id 或短名。
story 数据集照同一套规则演,比例是反的:112 条采用了 84 条,动了 69 处。收到条很短(每行 3 条,颗粒大、只铺一层);第二拍只去掉 28 颗(熄灭 22 颗,6 颗成灰烬);第三拍有 9 轮落下的记号比采用的颗粒还多。这是手写情境本来的样子(每条输入都编排过),不为它改说法,英雄数字现算为 69。f17、f27 两个定时整理的行没有颗粒,第一拍什么也不落,第三拍只硬切出回执:f17 是 r17,带一处只改内容 ◇1;f27 是“检查”。收得最多和动得最多是同一行 f01,所以只画一条标注:“收得最多,也动得最多(9 条 → 16 处)”。story 的改动含子项(69 处里 28 处),子项记号沿用现在的画法;没有本轮输入直接推动的 33 处,在第三拍末尾自己落下。
审阅定下的(原为待定,2026-10-19)
- 此刻之后的行: 仍隐藏,“此刻”语义不变;顶层直接打开且不带
?frame=时默认落在最后一帧。 - 开场: 只在顶层直接打开、不带
?frame=时演一次,任意键跳过,有“重演”;画廊缩略图、合页嵌入、从别处链来的(都带?frame=或在 iframe 里)一律不演。 - 英雄数字: 131,不用 63%。
另外定下:侧栏就是详情卡(不另起浮卡盖住谱),展开与收回的时序照 ix.mjs 的 IX 常数;“去回答 →”链到当前查看那一帧的 D,只给该帧仍开着的问题;交互接 ix.mjs,钩子 window.foldNotation 与 foldDial 同名同义;换帧走节拍过渡,不硬切。
4 motion:怎样做的
全部改在 notation.mjs 与 notation.css,页面多链一份 ix.css;只读回放,数字由页面现算,与第 2 节逐项相等(composite:3196 / 1177 / 131,进了动了的那几件 541 条,f20 的 234 颗 = 66 + 111 + 57、11 颗流到 3 个记号、55 颗流进 ▼1 ◇8;story:112 / 84 / 69,42 条)。
- 两层的画法。 颗粒、灰烬、流痕、外框、上沿朱红与合计条画在行下面一块 Canvas 2D 上(按设备像素密度),行、记号、文字、合计行与账仍是 DOM。颗粒尺寸照第 3 节:composite 三层、单位 2.18 px、判定前 1.4 px,story 一层、单位 8 px、6 px。采用的颗粒判定后转墨时长到 1.73 px(composite),1× 屏上也读得出是墨,仍是一颗一条。判定前的灰比信息层的字和线都浅。
- 一颗颗粒进了哪件。 先找本轮批次声明里引用它的那条,没有就用
relatedIds[0];从这件事起沿合并与父项往上找,第一件本轮“动了”的就是它的记号,否则第一件变小或只改内容的就流进 ▼/◇,否则在改动列前散掉。按这条规则算出的数与第 2 节一致。 - 一个时钟。 开场、换帧写入的一行都走同一个可停的动画时钟;悬停或展开时它停住(
ix.mjs的 pause / resume),所以开场中途悬停,开场停住,卡上只写已经落定的事(输入还在写 / 判断还没出来 / 改动还没落定;两周中位在开场结束前不写)。退回时淡出的行用墙上时钟,展开一行把此刻移回去时它们照常淡出。 - 开场。 时间点照第 3 节:0.4 秒第一颗颗粒落下、列头“收到”转墨;4.3 秒判定;5.3 / 5.6 秒账上采用与分项硬切;6.6 秒起流痕逐行错开 0.06 秒;8.9 秒没有输入推动的记号先空心再填实;9.4 秒问题与回执硬切;9.7 秒起两条标注各 0.3 秒;11.4–12.0 秒账上三个数落进合计行,旁白逐行淡入。行数不同时错开间隔按比例收紧(story 36 行,读一拍仍在 4 秒内写完)。每颗流痕起跑有 0–0.08 秒的错开,“最后一道到达的那一帧记号硬切出来”才有先后。信息层的几次硬切处,概念层 300 毫秒内降到六成。账放在旁白列里避开两条标注的行:composite 在上半,story 的标注在 f01,账放到下半。
- 缩短版。 0–0.3 秒铺开、条数硬切;0.35 秒判定;0.6–0.9 秒流痕跑到记号,记号到达那一帧硬切;0.9 秒问题与回执硬切,旁白 0.2 秒淡入;合计行跟着各拍更新。一次走过几行时逐行错开(总共不超过 0.3 秒)。←、连播、刻度条拖动、合页顶栏选帧都走这一套;减弱动效时直接到终态。
- 交互接
ix.mjs。 舞台是谱加侧栏(.st-body),隐藏按钮放在谱的最前面。一行(row:i)、一个记号(item:<id>@i)、一个问题记号(ask:<id>@i)、合计行(total)各是一种“一件”。悬停:概要卡贴在行上方(放不下翻到下方),引线从行尾的条数或记号连到卡;其余行、记号与文字降到七成、概念层降到四成;悬停记号时这件事每一处记号、追踪列的圆标与每一行里进了它的颗粒保持原亮度。展开:模块的详情卡贴在侧栏的位置、用侧栏的样子,从引线接上的那一点展开;一行沿本行向右推出 8 px、此刻的上下两条线加粗,引线沿本行基线画到侧栏;一个记号推出 8 px,引线走到追踪列、沿它经过这件事的每一处记号(经过时圆标转墨),从此刻那一行出到侧栏。时序全用IX常数。 ix.mjs加了两个可选项(只增不改):宿主可以给peekHTML(f, o)与detailHTML(f, o),卡里写自己的内容;缺省仍是事项卡,整理脉冲与份额拨盘不受影响(两页 16 项重跑都过)。- 钩子。
window.foldNotation:replay / skip / seek(t) / resume / stepAt(dt) / release / census(i) / totals(i) / geom(name),以及ix.hooks()的hover / unhover / open / close / ixAt / ix / paused / pointOf / ixState,与foldDial同名同义;名字可以是帧 id(f20)、row:20、事项 id 或短名(它到此刻为止最近一次改动)、total。perf() / resetPerf()量开场的帧间隔。
5 direct:看过之后改的
按 1440×900 在 composite 与 story 下逐拍截图看过(seek 停在 2.2、4.6、6.4、7.3、10.4、11.75 秒,缩短版用 stepAt 停在 0.2、0.5、0.8、1.05 秒),改了这些:
- 大括号改成横的。 第 3 节写“合计行左侧画一道大括号”。照做后竖线贴在间隔列与收到条之间,story 的“13h”“15h”与它挤在一起,也像一根多出来的列线。改成收到列下、合计行上方 12 px 空隙里的一道横括号,尖朝下指向合计条:意思不变(每行的条收拢到合计条上),不和任何字争地方;开场收束时 0.4 秒画完,之后一直在。
- 侧栏在展开时先让开。 详情卡从左缘展开的 0.3 秒里,底下的侧栏概览露在还没展开的右半边,两层相似的字错开叠着。现在从切点起侧栏自己的内容让开(侧栏空白一瞬),卡从引线接上的那一点展开;收回时卡收完的那一帧概览回来。
- 流痕更淡。 第一版一行几十道流痕叠成一条深灰带子,比墨还抢眼;拖尾降到两成、头部降到七成五,汇向记号的形状仍在。
- 采用的颗粒判定后长大一点(见第 4 节),1× 屏上采用段原来读成灰。
- 合计行的骨架。 “两周 / 合计”与空框从开场一开始就在,数在收束时落进去。
- 开场中两周中位不写,免得卡上提前说出后面的事。
没有照第 3 节做的,或另做了选择的:
- ▼/◇ 小计不单独出卡,算在这一行里(一行的卡写了“另有 ▼1 ◇8”,展开的“这一轮”按类列出它们)。
- 问题记号逐个可悬停只在一行不超过 6 个时;超过时改成“◆×6”那样的小计,悬停它就是悬停这一行。
- 记号的命中区只上下各放宽 3 px;左右挨着的记号本来就贴在一起,再放宽会互相抢。
- Tab 从此刻那一行往前走,最后是合计行。按回车展开一行会把此刻移到那一行、其后的行隐藏,从此刻往前走才不会一按回车就把后面还没走到的行藏掉。
- 展开一行的“这一轮”比侧栏默认多两节:每条流的采用数,以及进了动了的那几件的输入(每件最多三条标题);其余与默认的“这一轮”相同,问题旁都有“去回答 →”。
6 ship:验证
本机另起服务(4191 端口;4173 被占用),Chrome 无头。
node scripts/check-ix.mjs --base http://127.0.0.1:4191 --only notation与--data story:16 / 16 都过。scripts/check-ix.mjs的 PAGES 加了批次记谱一项(这一页没有概念卡,用tabFrom让 Tab 从顶栏的刻度条起)。整理脉冲、份额拨盘重跑也都是 16 / 16。node scripts/shoot-forms.mjs --only notation,all --gpu --check all-sync:composite 与 story 都无报错,合页逐节跟上顶栏选的帧。make check(启动文件、文档链接与格式)与npm run site:build通过。- 帧率:
--gpu、1440×900,开场 12 秒 718 帧,平均 60 fps,帧间隔 p95 18 ms;每帧脚本平均 1.1 ms、p95 1.7 ms(3196 颗同屏、流痕最多时)。静止时不重画。 - 减弱动效:不演开场,切帧后 60 ms 已是终态,R 直接到终态。
- 画廊卡片的“默认帧”改读
catalog.mjs的last(composite 显示 f28),缩略图与合页嵌入停在最后一帧、不演开场;体验站预览图site/previews/notation.webp按 docs/development.md 重拍(截图前foldNotation.skip()跳到终态,命令已写进文档)。
还差什么:
- 收到一栏仍依赖演示数据的逐条处置,批次说明仍借用回放旁白,契约里都没有(同 review-b)。
- 只有 1440 宽的横屏版式;三层颗粒在 1× 屏上仍偏细。
- 帧率只在本机 Chrome(无头、开显卡)量过;Safari 没有在本轮重看。