Skip to content

结绳 · 动起来之前:参考、数据、故事 ​

状态:2026-10-28 按六步流程(reference → data → story → motion → direct → ship)做第四轮候选 06 结绳。第 1–4 节写于动手之前,定下论点、英雄数字、三拍与交互;第 5–6 节是做完之后的实现、出入与验证。页面是 sketches/khipu.html:复盘工具,不承载作答。两层法则与交互标准见 CONCEPTS.md,同类页面的范本是让位图,交互取共用模块 motion/ix.mjs;候选的来历见 INSPIRATION.md。

1 参考:草图怎么讲,哪里讲不动 ​

起点。 探索代理的静态草图(本机 scratchpad,不入库)读手写情境 f26:一根粗主绳横过上方,八根垂绳挂下来,十位的单结与个位的长结对齐成两道横带;六根有子层的垂绳在中段分叉出副绳,副绳上有自己的结和“≈2.3”;主绳上方两根合计绳写工作 55、家里与生活 6。用户看过说可以往下做。

哪里讲不动。

  1. 乘法藏在注脚里。 “≈0.21”是一行 9.5 px 的灰字,看不出它是 24 × 6/64 × 6/64 乘出来的,更看不出上面一层一变,下面每一根都跟着变。
  2. 子层自己的 64 份看不见。 发布的子层“用了 12、留白 52”只有一行“子层用 12/64”;副绳挤在约 110 px 的槽里,名字截到四个字(“闲置测试”“测试约1”),三层叠在一起。
  3. 留白没有绳。 根层 64 = 55 + 6 + 3 要读者自己加;子层的留白(发布里 52 份)根本没画。
  4. 组是写死的。 草图用一个 WORK 集合分组,换到 composite 就不成立。
  5. 没有时间。 静态的 f26 看不出“子层一结没动、折合变了四倍”:那发生在前一轮 f25,发布从 6 份升到 24 份。

参考图(.local/inspiration-r4/,逐张看过)。

  • Locke 1923 图版 XXI(#98):黑底白绳,一根横向主绳,下面一排垂绳,结打在不同高度;垂绳 d 的中段分出一根副绳;右栏是 1–9 圈的长结,圈数就是数字。
  • Locke 图 45(#98):一根合计绳横着绑在四根垂绳上方,记的是它们的和。
  • Larco 博物馆实物(#98):几百根绳摊成半圆,远看结聚成几道同心带:同一数位的结离主绳一样远。
  • 结的读法图:2–9 是长结的圈数,1 是 8 字结。
  • d3 圆填充(#97):父层只画轮廓,子层在里面;跨层比较要换算成有效份额(父 24 份 × 子 6/64 = 2.25 份),否则会误读。
  • Chrome View Transitions(#80):点中的缩略图自己长成主播放器,其余缩成一列窄栏;返回时缩回原格。

搬什么,不搬什么。

要素出处搬过来不搬
主绳、垂绳、副绳、合计绳#98根层、事项、子层、一组的和;每一层都是“一根横绳挂一排垂绳”绳的捻纹、毛头、颜色与立体感;平涂线、几何结
结的高度 = 数位Larco 的同心带十位的单结在靠主绳的一带,个位的长结在下一带,所有绳横向对齐;每层自己的两道横带百位:份额最多 64
长结的圈数图版 XXI 右栏、读法图个位 n 圈是一枚圆角胶囊加 n−1 道纸色细线;1 用 8 字结(两个相接的小圈);十位是实心圆点,一点十份绕绳的透视
副绳从垂绳分出图版 XXI 的 d子层从垂绳下端的一根短横绳挂下;这根短横绳就是子层的主绳,粗细等于它上一层垂绳从中段斜分:会和结的横带打架
合计绳图 45工作组、家里与生活组各一根,横在主绳上方,用虚线系到成员垂绳缠绕的绑法
有效份额#97每根副绳下写“≈ 折合根层”的份数圆面积:第二轮已因难以并排比较放弃
钻取的过渡#80点开有子层的垂绳,它下端的短横绳长成子层画框的主绳,根层缩成顶上一条窄带,每根绳的 x 不变;返回时原路缩回换页、改地址
切点、第一个动作、缓动拨盘、让位图日期与旁白在切点硬切;讲述时切点后 0.4 秒再动;位置用 ease-out(过冲 3.7 %)循环:复盘工具讲一遍就停

概念层:一成以内,静止为零。 只有两种灰,都贴着绳走,个数与信息层一致:

  • 打结:换轮时份额变了的那根绳,旧结先转灰、一圈圈退掉,新结再一圈圈绕上(灰),十位一颗一颗落下;圈数就是个位,灰点数就是十位;绕完那一刻转墨,数字硬切。
  • 折合:一根有子层的绳在根层变了份额时,一颗灰珠从它的结沿绳往下走,过短横绳分到每根副绳,经过“≈”时那个数硬切;走到最深的一根就停。

不飞点,不铺纹理;任何时刻都在变动的那几根绳附近,面积远小于一成。

2 数据:子层怎样折合,组怎样分 ​

用哪一帧。 两个数据集都停在 f26,帧号是页面常量(scenario 里没有 sketchFrames.khipu,不改数据):

  • 手写情境(story,本形式的默认样张):frames[28],f26,10/21 17:30「已从两店扩到五店,暂不全量开放」,r26。
  • composite:frames[26],f26,8/16 00:00「车道下的铜管裂了」,与原版、A/B/C/D、腕上一瞥取的同一刻。

口径。 用 node 直接导入 variants/shared.mjs(records、currentAsk)和 sketches/stage.mjs(askLife、responsesIn)现算,脚本只在本机。

  • 层:records[i].state.root 是根层,items[].children 是这一件自己的一层(budget 64、used、items),逐层往下。
  • 折合根层:根层的一件就是它的份数;子层的一件 = 父件的折合 × 自己的份数 ÷ 64。显示时 1 份以上留一位小数,不到 1 份留两位(2.25 写 ≈2.3,0.2109 写 ≈0.21)。
  • 留白:每层 64 − used;根层的留白画成主绳末端一根虚线垂绳,子层的留白只在画框里画。
  • 分组:一件根层事项的组由它两周里所有版本的 sources 定:composite 读来源事件的 stream,手写情境没有流,按渠道归(团队、客户、监控、部署、财务、招聘这类是工作,家人是家里,物业、随手记、定时提醒这类是生活;本人的“用户消息”和“官方资料”不计)。工作与社区并为“工作”,家庭与生活并为“家里与生活”,取多数;都没有的归“其余”(两个数据集里都没有)。组按整份回放定,所以一件事不会中途换组。子项跟着根走。
  • 问题:askLife[i].open 是这一帧开着的问题(含子项上的),answered、lapsed 是这一帧关上的;开着的问题在它挂的那根绳上打一枚朱红小方块。
  • 换 id、合并、撤下:逐帧按 id 对应;撤下的绳缩回它的横绳;并入的先滑到 merged_into 那根绳再缩回;同一件事换了新 id 不猜,当作新绳。子项的 parent_id 两周不变(kit.mjs 也这样假设),子层里撤下的绳同样缩回(手写情境有 8 个子项快照是撤下状态,没有并入)。

composite:两周没有子层。 29 帧里 records[i].state.root.items[].children.items 全是空的,Agent 批次没有给任何一件事分子项;所以 composite 只画根层与分组合计,页面写明“这两周没有子层”。f26 根层 14 件:家里与生活 40(主水管 24、猫看病 6、后刹 3、母亲换水管 3、Ture 发烧 1、开学文件 1、漫画 1、风扇 1),工作 23(FTPS 计时 6、待审 PR 6、master CI 6、65KB 上传 3、Kerberos 1、rc1 1),留白 1。根层最多 26 件(f17,8/11 12:00);两组差得最远是 f20(8/13 00:00),家里与生活 59 : 工作 5。

手写情境:三层。 37 帧(cold-00 至 f34),f01 起 34 帧有子层;最深三层(根层 → 子项 → 子子项),只在发布 › 旧机验证下面。f26 的整根结绳(records[28].state.root):

根层份数组子层用了子层里的件(份数 / 64 · 折合根层)
发布24工作12旧机验证 6 · ≈2.3(其下回滚开关 6 · ≈0.21,旧机筛选 6 · ≈0.21);导出模板 6 · ≈2.3
费用12工作30本月上限 12 · ≈2.3;构成 12 · ≈2.3;测试环境 6 · ≈1.1
试用6工作12暂存列 6 · ≈0.56;新表范围 6 · ≈0.56
链路6工作——
筛选6工作12连点与补偿 6 · ≈0.56;INP 样本 6 · ≈0.56
苏禾1工作——
妈妈3家里与生活15见面 12 · ≈0.56;东馆行程 3 · ≈0.14
厨房3家里与生活12照片 6 · ≈0.28;回访 6 · ≈0.28
留白3———

合计:工作 55、家里与生活 6、留白 3,64(records[28].state.root.used = 61)。折合根层最小的不是最深的:妈妈 › 东馆行程只有 ≈0.14;最深一层的两件各 ≈0.21。

这根绳两周没动,折合跟着变了八倍。 回滚开关在旧机验证的子层里两周都是 6/64,旧机验证在发布的子层里两周都是 6/64;回滚开关最后一次改写是 10/16 10:00(f12,只改了措辞)。它折合根层 = 发布的份数 × 36 ÷ 4096,只随发布变(records[i] 里 launch → launch-gate → launch-rollback 的 share):

帧发布回滚开关折合根层
f01–f04120.105
f05–f06、f08–f1060.053
f0730.026
f11–f14120.105
f15–f1830.026
f19120.105
f20–f2460.053
f25–f27240.211
f28–f29120.105
f30–f3360.053
f3430.026

两周里最大的一次就是 f25:发布 6 → 24,回滚开关 0.053 → 0.211。发布的子层从 f05 起一直是 12/64(旧机验证 6 + 导出模板 6;f01–f04 导出模板是 12,共 18),所以 f25 发布在根层翻了四倍,子层里一结没动:24 份根层里装着 12 份子层,折合 4.5 份,其余 19.5 份是它自己的留白。

故事用的三帧。

帧根层组问题来源
f24(frames[26])10/20 19:30「暂存列已隐藏,本月API预算定为5200元」费用 12、试用 12、筛选 12、发布 6、妈妈 3、厨房 3、苏禾 1、链路 1;留白 14工作 44、家里与生活 6开着:苏禾改约周四 19:30(ask-26)records[26].state.root、askLife[26]
f25(frames[27])10/21 09:15「补偿任务验证通过,是否再开三店待决定」发布 6 → 24(critical),筛选 12 → 6;留白 2工作 56、家里与生活 6新登记:今天是否加开三店?(ask-32,挂在发布上)frames[27].asks.launch、frames[27].batch.declarations 里 launch 的 weight: critical
f26(frames[28])10/21 17:30「已从两店扩到五店,暂不全量开放」试用 12 → 6,链路 1 → 6;留白 3工作 55、家里与生活 6答掉 ask-32:17:09 你回复了 Agent(加开三店);仍开着 ask-26frames[28].events[0].feedback[0] = {ask: 'ask-32', action: 'add-three', via: 'reply'}

回放里有、图上还没用的。 每层的 budget 都是 64;子项也有自己的 reason 与 sources,只放进详情。回放里没有的:“为什么这件事要分子项”的批次级说明,以及子层份额与根层份额之间的任何约束(一件根层 24 份的事,子层可以只用 12)。

3 故事 ​

一个点。 一件事里还有自己的 64 份;子层里的一份值多少,由上面每一层乘出来。(手写情境:10/21 早上发布从 6 份升到 24 份,它子层里一结没动,最深的回滚开关折合根层从 0.05 变成 0.21;而发布这 24 份往下,子层 64 份只用了 12。)

英雄数字:0.21。 最深一层的一份折合根层是多少:24 × 6/64 × 6/64 = 0.21 份。它是两周里这个数最大的时候(f25–f27),离回滚开关自己最后一次被改写(f12)已经 13 轮。放在侧栏顶上,字高 56 px、字重 500,等宽;下面一行小字写乘式“发布 24 × 旧机验证 6/64 × 回滚开关 6/64”,再一行写“子层一结没动,只是上面变了”。讲述时它在折合的灰珠走到最深那根绳的一刻从 0.05 硬切到 0.21;逐帧时在落定一刻硬切。第二个数“12 / 64”写在画框的抬头与侧栏的子层清单里,不另做大字。

三拍(手写情境共约 9 秒,讲一遍就停在 f26 的发布画框里。帧号是页面常量,不写进数据)

拍时长与帧发生什么观众此刻在动 / 只在节拍上落定
00–0.4 秒,f24整根结绳静止:发布 6 份,下面挂着旧机验证与导出模板,再下面两根回滚开关、旧机筛选写着 ≈0.05;侧栏英雄数字 0.05一根结绳什么也不动
1 根层四倍0.4–2.5 秒,f25切点硬切日期与旁白;发布那根绳上,个位的 6 圈长结转灰、一圈圈退掉,十位落下两颗灰点、个位绕上 4 圈,转墨,“6”硬切成“24”;筛选 12 → 6 同样重打;工作组的合计 44 → 56、留白 14 → 2 硬切;发布绳上落一枚朱红方块:今天是否加开三店?根层给了发布四倍动:退结、绕结、各绳滑到新位置。落定:数字、合计、朱红方块
2 一结不动2.5–5.0 秒,f25一颗灰珠从发布的结沿绳往下,过短横绳分成两颗:旧机验证、导出模板的结都不动,“≈0.56”硬切成“≈2.3”;再从旧机验证往下分成两颗,“≈0.05”硬切成“≈0.21”,两根描 1.4 px 墨框;英雄数字同一刻 0.05 → 0.21;停 0.8 秒子层一结没动,折合全变了动:灰珠。落定:每个 ≈ 在灰珠到达时
3 长成画框5.0–9.0 秒,f26换轮:试用 12 → 6、链路 1 → 6 重打,朱红方块随“加开三店”落下而撤掉;落定后 0.3 秒,发布下端的短横绳长成子层画框的主绳(0.55 秒),根层缩成顶上一条窄带,其余垂绳缩成短桩只留份数;画框抬头“发布 · 根层 24 / 64 · 它自己的 64 份:用了 12 · 留白 52”,留白 52 是一根虚线垂绳,五颗空心点加两圈空心长结;回滚开关、旧机筛选的 ≈0.21 仍描着框;停 1.5 秒24 份里只用了 12;最深一件折合根层 0.21 份动:打结、画框长出。落定:画框抬头与留白绳

讲完后页面就是普通的复盘工具,停在 f26 的发布画框里(地址栏 frame=28);≈0.21 的墨框留到第一次交互为止,“← 根层”或 Esc 收回画框。

一轮的时间表。 讲述按全速;空格连播是半速(每个时刻 × 0.5),←/→ 前进一帧用短版:

  • 0 切点:顶栏日期、帧号与旁白一帧硬切。概念层为零。
  • 0.40 第一个动作(短版 0):各绳滑到新位置,新绳从横绳往下长出,撤下的缩回、并入的先滑到去向再缩回(0.45 秒,ease-out,过冲 3.7 %;短版 0.25 秒)。
  • 0.85 退结(短版 0.25):份额变了的结转灰、一圈圈退掉,0.15 秒(短版 0.07)。
  • 1.00 绕结(短版 0.32):个位一圈一圈绕上,十位一颗一颗落下,全是灰,一共 0.45 秒以内(短版 0.18)。
  • 1.50 落定(短版 0.50):结转墨;垂绳的份数、合计绳、主绳的“用了 · 留白”、朱红方块一帧硬切。概念层只剩灰珠。
  • 1.60 折合(短版 0.52):只在根层份额变了、又有子层的那几根绳上;灰珠每层走 0.35 秒(短版 0.15),带 0.15 秒拖尾,经过“≈”时硬切;走完概念层为零。
  • 之后静止,留着读;没有折合的一轮共 2.1 秒,有两层折合的约 3.2 秒。

往回退(←)与一次跳多帧:各绳 0.25 秒滑到位,结与数字在 0.12 秒硬切,不打结、不走灰珠:倒放会像把份额要了回来。

两层。 信息层(九成以上):主绳、垂绳、横绳、合计绳与它们的粗细;墨色的结;份数、合计、≈、名字;朱红方块;画框与抬头。只用墨黑与灰;只在节拍上落定。概念层(一成以内,静止为零):退结与绕结的灰、折合的灰珠。只用灰,不带文字与数字,个数与信息层相同。规矩:信息层动的时候概念层不另起动作;概念层不暗示信息层没有的事实(份额没变的绳不打结,根层没变的子层不走灰珠)。

动起来之前的约束。

  • 只用墨黑;绳的粗细只分层级:主绳 3.2 px,根层垂绳与它的短横绳 1.8 px,子项 1.1 px,子子项 0.8 px;画框里放大的只是结与字,粗细不变。
  • 朱红只给一种含义:此刻开着的问题,一题一枚方块,挂在问题所在的那根绳上。
  • 不拟物:不画捻纹、毛头、阴影;结是圆点、圆角胶囊与两个相接的小圈。
  • 绝不动:结的横带高度、每层的横带对齐、主绳的位置(画框之外)。
  • 只读回放:组、折合、问题都现算,故事的帧号是页面常量;不改 scenario.mjs、shared.mjs、engine.mjs 与演示数据。
  • 两套数据都能跑:composite 没有子层,就没有折合、灰珠与画框,也不讲;页面在图里与侧栏写明“这两周没有子层”,并链到手写情境。
  • prefers-reduced-motion:切帧、展开与画框直接到终态;讲一遍直接跳到终态。
  • 合页顶栏选帧、画廊缩略图、带 ?frame= 与从别处链来的都静止:立即停讲述、收卡、收画框,直接到那一帧的终态。
  • 性能:一张 canvas 画整根结绳,只在有东西在动时重绘;换轮与长出画框保持 60 fps。

4 交互 ​

与份额拨盘、让位图同一套标准,悬停卡、弹出、引线、详情卡的时间与样式取 motion/ix.mjs;结绳只提供几何。悬停与展开都让讲述与连播停在当前时刻,收起后 0.4 秒接着走。

  • 悬停
    • 触发:鼠标移到任一根绳上(垂绳、副绳、子子绳、合计绳、留白绳;命中区在画出的绳、结、份数与名字外再放宽 10 px,相邻两根之间按最近的那根算);Tab 到它(每根一个隐藏按钮,根层从左到右、每根后面跟着它的子绳,再是留白与合计绳);触屏轻点一下。
    • 出什么:纸底墨框的概要卡,挂在绳旁朝空的一侧。垂绳:组 · 名字与份数、这一轮怎么变、子层用了几份、开着的问题;副绳:所在的层(“发布 › 旧机验证”)、份数 / 64、折合根层与乘式、这一轮怎么变;合计绳:组的和与成员;留白:n / 64。
    • 其余怎么变:这根绳、它的祖先与子孙保持原亮度,其余降到七成;概念层降到四成。
    • 节拍:停在轮中途时,落定之前写“本轮还没落定”,不提前说出新份数。
  • 展开
    • 触发:点绳或它的概要卡,聚焦后 Enter 或空格。
    • 动什么:0 秒切点,顶栏旁白改写成“这一件 · f26 · 10/21 周二 17:30 / 发布”,侧栏切到“这一件”;0–0.3 秒这根绳(连同它的结与子绳)沿自己往下弹出 8 px,挂点不动、上面一段被拉长(合计绳往上弹);0.4–0.6 秒引线从结的横带水平出到卡;0.6–0.9 秒详情卡从引线末端展开。其余降到 18 %。卡放在绳旁空的一侧,不盖住这根绳的子树。
    • 详情卡(这一轮这一根):轮次与组;标题;份数 / 64 与这一轮的变化;结的读法(“十位 2 结 · 个位 4 圈”);有子层的写子层用了几份、留白几份,逐件列出份数与折合根层,并给“长成子层画框 ↘”;副绳写乘式;此刻开着的问题与“去回答 →”;这一轮关上的问题写怎么关上的;你的回应;摘要。
    • “去回答 →”:只给当前帧仍开着的问题(askLife[i].open,含子项),链到同一帧的 D:../../index.html?variant=d&frame=<帧号>(帧号用 f26 这样的 id,经 withDataset 带上 ?data=)。答掉或失效的写动作句或“随改写失效”,不给链接。
    • 面板“这一件”:两周里这一根的份数逐轮小条,副绳另有一行折合根层的小条(回滚开关那一行就是上面的表);子层用量逐轮;问题开关的那几轮;摘要、依据、来源与版本(itemDetailHTML,折叠)。
    • 收回:Esc、点空白、再点同一根:卡 0.2 秒收起,引线 0.15 秒缩回,绳 0.2 秒落回,一帧切回,0.4 秒后接着播。←/→、选帧、播放与合页选帧立即收起。
  • 子层画框(钻取,借 #80)
    • 触发:有子层的垂绳下端那枚“子层 12/64 ↘”小签;详情卡里的“长成子层画框 ↘”;讲述第 3 拍。
    • 动什么(0.55 秒,ease-out):主绳升到顶上一条窄带,各垂绳缩成 18 px 的短桩、结淡出、只留份数,x 不变;合计绳淡出;被点开的那根绳从窄带一直垂进画框,它下端的短横绳拉长成画框的主绳;子绳滑到画框里的新位置,结从 0.78 倍放大到 1 倍,名字从四个字换成全名;画框的边与子层的留白绳淡入。别的绳的子绳缩回、淡出。
    • 画框里:子层自己的十位、个位两道横带,名字下写“≈ 折合根层”;子子绳仍挂在下面。悬停与展开照常,对象是画框里的绳与顶上的短桩。
    • 收回:画框左上角“← 根层”、Esc(有卡先收卡)、讲述之外的选帧跳到这件事没有子层的帧:原路 0.45 秒缩回。←/→ 时这件事还有子层就留在画框里换轮。
  • 讲一遍:只在顶层直接打开、地址不带 ?frame=、不是从别的页面链过来时演一次,任意键跳过到终态;工具栏「▶ 讲一遍」可重演,讲的时候变成「跳过 ▸▸」。只有手写情境有三拍。
  • 脚本:window.foldKhipu 给出与 foldYield 同名同义的 hover / unhover / open / close / ixAt / paused / pointOf / nearOf / pick,名字可用条目 id、短名、组名或“留白”;另有 tell / skip / tellAt / play / stop / telling / playing / settled / phase / drill / rise / view / perf / resetPerf。window.foldSketch 照旧。

5 实现与出入 ​

文件。 sketches/khipu.html、khipu.mjs、khipu.css;页框是第二轮的 stage.mjs(帧条、侧栏、键盘、合页跟帧),页头改写成“FOLD · 第四轮 · 关系的画法”。注册:catalog.mjs 的 sketches 末尾(no 11,sample: 'story')、scripts/check-ix.mjs 的 PAGES 末尾、scripts/shoot-forms.mjs 的 FORMS、prototype/README.md 形式表、CONCEPTS.md「第四轮:关系的画法」的 06 卡、合页 all.html 的第四轮一组、体验站 site/.vitepress/theme/prototypes.mjs(带 ?data=story)与预览图 site/previews/khipu.webp。

画法。 一张 canvas 画整根结绳(像素比最多 2)。每一帧、每个视图(根层,或某一件的子层画框)先建一份场景:每根绳的 x、挂点、两道结的横带、份数、名字、≈、朱红方块,各层的短横绳与合计绳。换轮与长出画框都是两份场景按稳定的键(p:<id>、bar:<id>、top:<组>、blank、blank:<父>)插值,所以“同一根绳位置连续”是同一个键的两个位置;只在有东西在动时重绘,静止时不画(实测静止每帧脚本 0.06 ms,只有读卡与侧栏)。命中按画出的绳、结、份数、名字与 ≈ 的外框算最近的一根,放宽 10 px;隐藏按钮按根层从左到右、每根后跟它的子绳,再是留白与合计绳。

与第 1–4 节的出入。

  1. 名字往下挂。 第 4 节写“名字从四个字换成全名”,做出来横排在子层里仍然挤:f26 根层以下 15 根绳、一槽约 55 px,横排的名字互相压住、也压住 ≈。改成根层以下的名字都往下挂:汉字直排,字母与数字侧排(同 CSS 的 mixed 排法),截断用侧排的省略号;≈ 写在名字上方、个位横带之下,于是每层的 ≈ 也对成一道横带。有子层的绳,名字写在绳的右侧、短横绳之上,最多七个字。根层名字(发布、费用……)仍横排。composite 两周没有子层、一轮最多 26 根绳(f17),根层名字也往下挂,否则只能截成“mas…”。
  2. 横带按上限留位。 十位留六颗单结、个位留九圈长结的高度(一根绳最多 64 份),所以两道横带从第一轮到最后一轮都不动。结与字的倍数:根层 1.15、子层 0.85、子子层 0.7;composite 没有子层,根层用 1.45。第 4 节的“结从 0.78 倍放大到 1 倍”实为 0.85 → 1.15。
  3. 讲一遍的时间。 实测 9.3 秒:静 0.4 秒;f25 一轮 3.85 秒(落定在 1.9 秒,灰珠 2.0–2.7 秒走完两层,英雄数字与两根 ≈ 在 2.7 秒硬切到 0.21 并描框,停到 4.25 秒);f26 一轮 3.0 秒(试用的子层也走一层灰珠);画框 0.55 秒;停 1.5 秒。第 3 节表里第 1、2 拍的分界写在 2.5 秒、第 3 拍从 5.0 秒起,实际第 2 拍在 2.0–4.25 秒、第 3 拍从 4.25 秒起。
  4. 英雄数字下多一行。 最深那根绳这一轮自己没动、折合却变了时,侧栏写“这一轮它自己一结没动,是上面变了:发布 6 → 24”(f25);乘式里子层的名字截到六个字。
  5. 合计绳的系法。 每根成员一条虚线,从合计绳垂到主绳,不画到垂绳上,免得和十位的结混在一起。
  6. 画框抬头。 写在被点开那根绳的右侧,放不下时放左侧,不压住绳;“← 根层”按钮在画框左上角。画框长出时,只属于画框的东西(边、抬头、子层的留白绳)在框长到六成以后才淡入。
  7. composite。 图里在名字之下写一段“这两周没有子层”,侧栏顶上用同一段代替英雄数字;工具栏是“看手写情境的子层 →”(手写情境里是“真实语料(没有子层)→”)。没有讲一遍,也没有灰珠与画框。
  8. 按住 → 与一次跳多帧。 与第 3 节一致:0.25 秒滑到位、0.12 秒硬切,不打结、不走灰珠;键盘自动重复的 → 也按这一档,免得一连串的灰。
  9. 朱红方块。 挂在开着的问题所在那根绳的左上、靠它的横绳一侧,一题一枚。问题挂在子项上时打在子绳上:手写情境两周里开着的问题按帧数一共 131 次,52 次挂在子项上(12 个问题,如 f12–f21 挂在试用 › 远川暂存上的 ask-19、f20–f23 挂在费用的预算子项上的 ask-28,那一件先叫“建议预算5000元”,后来改写成“本月上限5200元”);composite 171 次全在根层。父件的详情卡列出它整棵子树上开着的问题。
  10. 卡上写帧号。 概要卡与详情卡的抬头写“f26 · 发布 › …”,不写“第 28 / 36 轮”:手写情境前面有两帧冷启动,序号比帧号大 2,和顶栏、侧栏对不上。
  11. 折合根层的逐轮小条按它自己两周的最高值缩放。 按根层的刻度(24 份起)画,0.03–0.21 份的一行看不见;副绳的详情卡在“份额 · 逐轮”(它在上一层的 64 份里,两周都是 6)下面也给这一行,正好是第 2 节那张表:绳没动,这一行在跳。

6 验证 ​

2026-10-28,本机自起静态服务(node,深监听队列,端口 4617;不用 4173),Chrome 无头。最后一次在 rebase 到 origin/main(f710b1a,含 #34 变形、#35 峰值保持、#36 驻龄、#37 拔河、#38 等待链)之后跑。

  • make check:33 个文件齐全,31 份维护中文档链接与格式有效。
  • node scripts/check-ix.mjs,两套数据、全部 18 页(pulse 到 tug 加结绳):story 与 composite 都全过,每页 16 / 16,D 手机首页 23 / 23,退出码 0。中途几次在机器满载(另有几个代理同时跑 Chrome、体验站在构建)时,结绳 composite 有两次单项超时(点击后 1.1 秒卡还没展开到底、悬停后 0.45 秒概要卡还没出),原版树图 story 有一次 Esc 超时,单独重跑都过。实测结绳悬停到概要卡出现 98–133 ms,只有页面加载时有一个 235–311 ms 的长任务(模块初始化)。
  • node scripts/shoot-forms.mjs --only khipu,all --gpu --check all-sync,两套数据:结绳与合页都没有页面报错,all-sync 没有失败项(合页里每一节都跟上了顶栏选的帧)。
  • npm run site:build:通过;dist/ 里有 prototype/forms/sketches/khipu.* 与 prototype-notes/forms/polish/khipu-motion.html。scripts/export-all.mjs 导出的单文件合页也带上了这一页。
  • 帧率(--gpu,?perf,foldKhipu.perf()):静止 60 fps、每帧脚本 0.06 ms(不重绘);点击展开 60 fps、脚本 0.41 ms(composite)/ 0.7 ms(story);→ 换轮 60 fps、0.28 / 0.66 ms;长出画框 60 fps、脚本平均 2.7 ms、p95 6.7 ms;讲一遍 9.8 秒 587 帧 60 fps、帧间隔 p95 16.8 ms、脚本 p95 2.3 ms。
  • 走查(本机脚本):两套数据从第一帧逐帧 → 到最后一帧、再 ← 退六帧、跳帧;手写情境每一帧的前三个有子层的件逐个长成画框,画框里悬停前六根(共 102 次画框、540 次悬停),根层每帧开合前两根与合计绳、留白;在发布画框里连按十次 → 仍留在画框(f18 → f28),Esc 收回;连播到末尾。没有页面报错。
  • 讲一遍:顶层直接打开、不带 ?frame= 时从 f24 起讲,约 9.3 秒后停在 f26 的发布画框里(地址栏 frame=28),两根 ≈0.21 描框;带 ?frame=、嵌在画廊与合页里、从别的页面链过来(referrer 是 forms/index.html)都静止在 f26;prefers-reduced-motion 时顶层打开直接到讲完的终态,→ 换轮、长出画框、展开详情都一帧到位。
  • 静帧(1440×900,本机 scratchpad 的 case-khipu/ship/):f26 根层、讲一遍的六个时刻(f24 静止、f25 绕结、灰珠、落定、画框长出一半、终态)、悬停与展开发布、画框里展开回滚开关、悬停工作组合计绳、f22 子项上的朱红方块、composite 的 f26、f17(26 根)与展开主水管。预览图 site/previews/khipu.webp 按工作环境说明的参数截(手写情境 f26 根层,640 宽,22 KB)。

还没做的。 手机尺寸没有专门排;只有根层的件能长成画框,子层里有自己一层的件(旧机验证)不能再单独点开,只在画框里画到它下面一层(根层视图画三层,画框里再画两层,手写情境最深三层,够用);“这件事为什么分子项、子层为什么只用了一半”状态里没有,图上只能画出来、说不出原因。