Skip to content

回应托盘 · 动画记录 ​

状态:2026-10-28,第四轮候选 08 回应托盘按第三轮的六步流程(reference → data → story → motion → direct → ship)做成完整形式(forms/trays.html,手机 390 × 844)。第 1–4 节(参考、数据、故事、手势与交互)在任何东西动起来之前写下,代码照它做;第 5–6 节是实现、与故事的出入和验证。原型探索,不是规范。起点是第四轮探索时的静态草图(只在本机),候选与灵感见 INSPIRATION.md 的 08 与 #78、#75;阅读加作答的范本是 D 手机首页(home-motion.md:答一件少一件、回执跟着条目走、长按代替悬停)与腕上一瞥(watch-motion.md:换轮三拍、自动讲一遍);两层法则与交互标准见 CONCEPTS.md。数字都由 scenario.mjs 回放现算,规则见 2.1;推算脚本只留在本机,不入库。

回应托盘与 D 有一处根本不同,故事要正面回答:D 把所有问题排成一样的卡片,按“多急”排;托盘也按多急排,但让每个托盘的高度说“要你做几下”。它仍是真正作答的地方:托盘里按下去就是回应,不加“去回答 →”。

1 参考(reference) ​

1.1 两张参考图读到了什么 ​

#78 Family 钱包的托盘(a-family-trays-frames.png,12 秒加油流程的 12 帧)。底下的设置列表压暗到约四成、一动不动;白色圆角托盘(半径约 30 px,四周离屏幕边约一指宽)浮在上面,贴着屏幕下沿。每一步只有托盘的顶边上下移动:选链四行勾选,顶边在屏幕中部;选金额六个块,矮一截;自定金额带数字键盘,顶边升到状态栏下面,最高;核对页列三条 $12 与合计,又落回中部;确认页是一个大数字 $36。底部的大按钮一直在同一个位置,文字从 Continue 形变到 Confirm。作者写明高度“故意各不相同,让进展一目了然”。

#75 Liquid Glass 的两段演示(a-liquidglass-tabbar-frames.png、a-liquidglass-fluidtime-frames.png)。音乐 App 一向下滚,约 0.4 秒内两层并成一行:四个标签只剩当前那一个,“正在播放”胶囊降进同一行。锁屏上通知一条、两条叠上来,“9:41”的字高从约四成屏高压到约两成,字宽几乎不变。玻璃的折射与高光是拟物渲染。

1.2 搬什么,不搬什么 ​

要素参考托盘
高度就是这一步要你做多少Family:每一步托盘高度不同搬,并且只让高度说这一件事:托盘高 = 头部 + 剩下几下 × 一排。点一下就完的只有一排按钮,最矮;推后、定时刻要先点开再挑,多一排;按掉一下矮一排,答完收成一行回执
托盘贴底、顶边移动Family:一个托盘贴在屏幕下沿不搬成一个贴底的托盘:五个托盘叠成一列,读的是从上到下,所以上沿不动(上沿是阅读的锚),变的是下沿,下面的托盘一次上移补位
按钮文字形变,共有的部分不动Family:Continue → Confirm,共有的“Con”不动搬成“按下的那个选项的字留下,成为回执的一部分”:回执写“主水管 · 你选了「整根挖开换」”,题目那一行不动
背景压暗、托盘浮起Family:列表压到四成只在长按与展开时搬:其余降到七成(ix 的标准);平时不压暗,每个托盘都能直接按
两层并成一行Liquid Glass 标签栏:0.4 秒并成一行搬:答完收成一行回执;两下的托盘点了第一下,第一排并进标题(成为标题旁的“推到…”小签),第二排升上来
数字给新来的让出高度Liquid Glass 锁屏:通知越多,9:41 越矮不搬:英雄数字的位置与字号永不变(D 的规则,也是第一眼的锚);数的值已经是“剩下多少”,再让字形变矮就是两种编码说同一件事
玻璃、折射、高光、手机壳Liquid Glass、两张图里的机身不搬。平面、纸白墨黑,托盘只有一道细框,不画机身

1.3 从 D 直接沿用的 ​

D 已经把“作答工具怎样动”做完了一遍,托盘沿用它的部件与纪律(home-motion.md 4.3、4.4),不另造一套:

  • 同一件事的托盘、回执、“已收到”那一行、详情是同一个盒子变形;盒子用上盖、中段、下盖三块拼,只动 transform(d-motion.mjs 的 makeBox、morph、reveal)。
  • 只在切点读一次布局,之后只写;揭开用纸色盖板 scaleY,不动 clip-path。
  • 回执按问题 id 认、跟着条目走;先“已保存”,下一轮整理回放后“已收到”(respond.mjs 的 createResponseStore、responseStatus)。
  • 手指保护:作答后 1.12 秒内,补位滑到手指下面的按钮不接连点。
  • 长按 0.45 秒、移动 10 px 取消;详情从托盘长出、下拉跟手收回、左缘右滑交给 Safari 当“收回”。

1.4 Safari iOS 的约束 ​

照 home-motion.md 1.4:约 60 Hz,只动 transform 与 opacity;阴影不做动画(托盘本来就没有阴影);不用 backdrop-filter;没有滚动锚定,补位时自己补 scrollTop;左缘右滑是后退、页面顶部下拉是 Safari 的回弹,首页都不绑;长按要关掉选字与放大镜(-webkit-touch-callout: none、user-select: none)。托盘页比 D 简单的一处:它是表单形式页,滚动发生在页内的一个滚动区里,地址栏伸缩不推动它。

2 数据(data) ​

2.1 规则 ​

量怎么算出处
开着的问题根层与子层条目里 currentAsk(s, id) 不为空的(属于条目的当前版本);本页答过的不算“开着”variants/shared.mjs currentAsk,与 D 的 compose() 相同
托盘的顺序D 的 urgencyKey:档位 → 24 小时内到期 → 期限 → 份额 → 排名variants/d.mjs
期限ask.due_at,没有则取条目 due_at(done 除外);“还有 N 小时”按本帧时刻算同 D
一个问题几下托盘上最短的那条路要点几下,按下表;按 normalizeAsk(ask) 的 expects 与 actions 现算respond.mjs normalizeAsk
英雄数字“N 下答完 M 个问题”N = 开着的问题各自还剩几下之和(两下的托盘点了第一下,就只剩一下);M = 开着的问题个数本页
托盘高度头部一排 + 剩下几下 × 一排;回执一行本页
本页回执按条目 id 与 updated_at 存,按问题 id 认:同一问题随改写重登记(ask id 不变),仍是你答过的respond.mjs createResponseStore;D 的 answeredRecord
让位回执在下一轮整理回放之后(responseStatus 为 acknowledged)离开托盘列,落到托盘下面的“已收到”一栏;问题关上而本页没答过:这一轮里有反馈指向它,写“你在原处……”(responseSentence),没有就写“随改写失效”,只在关上的那一轮列出forms/sketches/stage.mjs responseSentence;D 的 lapsedAsk

几下的口径(写在页脚,详情里“这一问要几下”一节逐件写明):

回应托盘里的控件最少几下托盘几排
confirm、approve、choose一排按钮,按一个就答完(approve 的“评论”、choose 的“回一句”是一句话的次要入口,不计)11
defer先点“推到…”,再挑一天(明早、周一、周三,或自己挑日子)22
schedule(时刻)先点“选时间”,再挑一个时刻(建议的时刻,没有就是两小时后、明早 09:00,或自定义)22
reply、schedule(一句话),有建议句一排建议句,点一句就发出11
reply(一句话),没有建议句先点“回一句”,写好再点发送;另要打字2 + 写一句2
reply(金额),有预填或建议金额一排金额,点一个就交11
reply(照片)“拍一张”;另要拍照1 + 拍照1

2.2 composite 的 f26:6 下答完 5 个问题 ​

08-16 00:00,旁白「车道下的铜管裂了」(frames[26].title)。与 D 的默认帧、份额拨盘第 3 拍是同一帧。

托盘 · 问题(ask.short)expects · 按钮几下档位 · 份额 · 期限
1主水管 driveway-wet · ask-23「主水管:挖开换还是免挖?」choose:整根挖开换 / 免挖穿新管1关键 · 24 份 · 8/17 10:00 前,还有 34 小时;本轮新登记
2FTPS 计时 issue-13587 · ask-24「#13596 计时修复:批准?」approve:批准(推荐)/ 要求修改;评论是次要入口1常规 · 6 份 · 无期限;本轮新登记
3待审 PR pr-reviews-0814 · ask-22「三个旧 PR 推到哪天?」defer:推到…2常规 · 6 份 · 无期限;f23 首次登记,f26 随改写重登记
4开学文件 signe-school-start · ask-21「Signe 的同意书签了吗?」confirm:已签1微量 · 1 份 · 8/18 23:59 前
5Ture 发烧 ture-pickup-0812 · ask-19「VAB 报了吗?」confirm:已申报1微量 · 1 份 · 无期限

合计 1 + 1 + 2 + 1 + 1 = 6 下答完 5 个问题,4 个点一下就完,“三个旧 PR 推到哪天?”要先点“推到…”再挑日子。f27 这五个问题原样重登记(ask id 不变),f28 加到 7 个、8 下;真实数据里到两周结束这五个问题都没有答(f28 旁白「两周结束:水管还没回」)。

2.3 两周里的分量 ​

compositestory
有开着问题的轮次28(f00 是空状态)36(cold-00 是空状态)
英雄数字4–9 下;最多 9 下 8 个(f08、f15);f23–f25 是最少的 4 下 3 个1–6 下,每一轮“几下”都等于“几个”
有两下托盘的轮次18 轮(f01、f02、f08、f09、f12–f17、f19、f20、f23–f28)0
不同的问题按分量一下 20 个(confirm 5、choose 9、approve 6);两下 6 个(defer 2、时刻 schedule 1、没有建议句的 reply 3)一下 39 个(choose 23、confirm 11、approve 1、带建议句或预填的 reply 与 schedule 3、照片 1);两下 0

story 是手写情境,作者给每个要一句话、一个数的问题都配了建议句或预填金额(“定本月 API 上限”预填 5000,建议 5000 / 5200 / 6400),于是两周里没有一个问题要点两下:托盘一样高。这是数据本身的样子,页面照实画,不为它另造分量。

2.4 讲一遍的样张 ​

compositestory
叠起f25 → f26:3 个托盘 4 下 → 5 个托盘 6 下;主水管、#13596 两个新问题进来,开学文件与 Ture 发烧换位(Ture 发烧从低档降到微量)f19 → f20:4 下 4 个 → 5 下 5 个;“周三演示前,开放范围怎么定?”随改写失效、离开,“定本月 API 上限”(一排金额)与“财务建议 5000 元”(批准)进来
按下替你按“#13596 计时修复:批准?”的“批准”(6 → 5),再按“三个旧 PR 推到哪天?”的“推到…”(5 → 4)与“周一”(4 → 3)。真实数据里没有这两步,是演示,讲完撤回,回到 6替你按“今晚拍一张修后的墙角”的“拍一张”(5 → 4):真实数据里 20:36 你回了照片(f21 的反馈 ask-24 · photo),按下后与你自己按的一样,可以撤回
让位(composite 的五个问题之后都没答,不演)f20 → f21:回执先填实写“已收到”,再落到“已收到”一栏;托盘列剩 4 个,数字仍是 4

3 故事(story) ​

论点。 一个问题要你做几下,托盘就有几排:点一下就完的最矮,要先点开再挑的高一排。按掉一下,托盘矮一排;答完,它原地收成一行回执,大数字减去那几下;下一轮整理读到了,回执才让出位置。高度、右栏的刻度和大数字说的是同一个数,永远对得上。

英雄数字:“N 下答完 M 个问题”的 N。 f26 是 6。它回答“答完这些要我花多少力气”,是这一页唯一的大字(等宽 68 px,位置与字号永不变),下面一行把它拆开:“4 个点一下就完;‘三个旧 PR 推到哪天?’要两下:先点‘推到…’,再挑一天。”四个形式的英雄数字连成一条:脉冲是这一轮收到多少(f20 的 234),拨盘是一共能放多少(64),D 是要你答几件(5),托盘是答完要几下(6)。

第一眼。 竖屏上方一个大“6”,下面五个圆角托盘按档位与期限叠成一列;四个一样矮,第三个高一排;每个托盘右边一栏写“1 下”“2 下”,栏里每一下一道灰刻度,和那一排按钮对齐;朱红小方块只标登记了的问题。

3.1 三拍 ​

拍时刻发生什么观众此刻
1 叠起一轮整理落地,约 1.0 秒切点换字;回执先填实;0.40 秒数字翻到新值;离开的托盘上收淡出,留下的一次移到新位置,新问题的托盘在自己的位置揭开,它们的高度已经是几下;0.96 秒新问题外框描一次朱红有 6 下,高的那个要两下
2 按下每一下即刻,0.4–0.5 秒按一个就完的:托盘原地收成一行回执(上沿不动,下沿上收),右栏的刻度跟着收掉,数字减一,下面的一次上移补位。两下的第一下:第一排并进标题,第二排升上来、由虚变实,托盘矮一排,数字减一;第二下同上收成回执每一下都看得见少了一排
3 让位下一轮整理落地,约 1.0 秒0–0.30 回执的空心勾填实、写“已收到”;0.40–0.88 回执一次下移,越过托盘,落进托盘下面“已收到”一栏、变成一行纸面上的字;托盘列补上空位;关上但不是你在这里答的,同样下沉,写“你在原处……”或“随改写失效”答过的去了哪,还剩几下

3.2 第 1 拍:叠起(换轮) ​

沿用 D 的换轮节拍表(d-motion.mjs 的 BEATS.turn),只换成托盘自己的元素:

时刻什么动怎么动
0切点页头的日期与轮次、托盘里的期限字样、“已收到”一栏的计数一帧硬切
0–0.30回执读到的回执空心勾填实,小字换成“已收到 · Agent 在 12:00 的整理中读到”
0.40–0.66英雄数字差 1 滚一格,差 2 以上一帧硬翻(D 的规则);下面一行同刻淡入新的拆法
0.40–0.64离场关上的问题若没有落点(条目已撤下)上收淡出
0.40–0.88主角回执与关上的托盘一次下沉到“已收到”一栏,每个晚 0.06 秒,越过别的托盘时在上层;后半段盒子从托盘变成一行纸面
0.48–0.80移位留下的托盘一次移到新位置;几下变了的(问题改写成另一种回应)盒子同时长高或收矮
0.60–0.96进场新问题的托盘从上沿往下揭开,错开 40 ms
0.96–2.56新问题新登记的托盘外框描一次朱红

3.3 第 2 拍:按下(本页作答) ​

按住时按钮缩到 0.97(0.12 秒);松手是切点。

时刻元素从 → 到
0–0.12托盘里的按钮、期限一行、右栏刻度淡出。题目那一行不动,是这个盒子的锚
0–0.20托盘的盒子上沿不动,下沿收到一行回执的高度
0.10–0.30回执一行淡入:“主水管 · 你选了「整根挖开换」· 已保存”;空心勾 0.12–0.30 描出;“撤回”在右端,焦点到它
0.20–0.46英雄数字减一,滚一格;拆法一行同刻换
0.20–0.52下面的托盘一次上移补位,不过冲
到 1.12手指保护补位滑到上一次触点下面的按钮暂不接点按

两下的第一下(“推到…”“选时间”“回一句”):0–0.12 第一排的按钮淡出,它的字移到标题右边成一枚小签“推到… ×”;0–0.24 第二排(日期一排,原来是虚线框、不能按)升到第一排的位置、虚线换成实线,盒子下沿收一排;0.12 数字减一;0.12–0.40 下面的托盘上移一排。点小签上的“×”或按 Esc 退回第一步:逆序,0.30 秒,数字加一。第二下照“按下”走。

撤回逆序、更快,0.40 秒:回执展开回托盘(两下的托盘回到两排),数字加上那几下,下面的托盘下移。

3.4 第 3 拍:让位(下一轮整理落地) ​

时间表就是 3.2 的“回执”与“主角”两行。两种变体:

  • 同一问题随改写重登记(composite f26 → f27,五个都是):回执照样填实、下沉到“已收到”,问题仍按你答过的算,不回到托盘列,数字不反弹(D 4.4 的规则)。
  • 问题改写成另一个问题(ask id 变了):托盘以新问题回到列里,题目下多一行“你 00:00 答过上一版:……”,你答的那一下不算进新问题。

3.5 讲一遍 ​

只在顶层直接打开、地址不带 ?frame=、没有 referrer 时演一次;任意键或轻点跳过,按下的键只用来跳过,跳过时直接到讲一遍的终态;页底“↺ 重演”在顶层打开时都有(重演先撤掉讲一遍替你按下的)。画廊、合页 all.html(followFrames)、体验站弹框与带帧地址都不演;prefers-reduced-motion 时也不演。

秒composite(约 7.8 秒)story(约 6.4 秒)
0直接落在 f25(不演过渡),页脚写“讲一遍 · 任意键或轻点跳过”落在 f19
0.9第 1 拍:换到 f26(4 → 6)第 1 拍:换到 f20(4 → 5)
2.8第 2 拍:替你按“批准”(6 → 5)第 2 拍:替你按“拍一张”(5 → 4),真实数据里的回答
4.4再按“推到…”(5 → 4)第 3 拍:换到 f21,回执填实、下沉到“已收到”
5.2再按“周一”(4 → 3)
6.8尾声:替你按的两下是演示,一起撤回(3 → 6)
终态f26,6 下答完 5 个问题,什么都没替你答f21,“拍一张”的回执在“已收到”里,可以撤回

composite 讲的是“一下和两下”,story 讲的是“按下和让位”:story 两周没有两下的问题(2.3),composite 在 f26 之后没有真实的回答(2.2)。两份数据各取自己有的,不编。

3.6 两层与约束 ​

  • 信息层(约 98%)。 英雄数字与拆法、托盘里的题目、期限、按钮、回执、“已收到”一栏、右栏的“N 下”。墨黑、灰阶加一个朱红。
  • 概念层(约 2%)。 右栏里的灰刻度:一下一道,竖向对齐那一排按钮;与托盘的高度、英雄数字共用同一个数(每一道刻度就是数字里的 1、托盘里的一排)。它只随托盘动:按掉一下就收掉一道,换轮时跟着托盘移。静止时零运动,不飞点,不出数字与朱红。
  • 颜色。 墨黑:题目、按钮、数字;推荐的选项是实心墨色。灰:刻度、细框、期限与小字。朱红:只给登记了的问题,题目前的小方块,与新登记那一轮的外框描一次;回执和“已收到”里没有朱红。
  • 绝不动。 英雄数字的位置与字号;托盘的排序规则(动画只表现 urgencyKey 排出的结果);题目一行在作答时的位置。
  • 每个元素一次只走一段。 一拍里每个元素从旧位置到新位置只有一段运动,不折返。
  • 不提前。 下一轮整理落地之前,回执不变实、不离开托盘列。
  • 预算。 只动 transform 与 opacity,切点读一次布局;一拍里同时动的元素不超过列表本身(composite 最多 8 个托盘)。

4 手势与交互 ​

手机没有悬停:轻点直接起作用,长按代替悬停(照 home-motion.md 3.5)。

手势在哪对应 ix 标准动效时长
轻点按钮托盘、详情底部作答(托盘专属)托盘原地收成一行回执,数字减一,下面一次补位0.52 秒;手指保护到 1.12 秒
轻点“推到…”“选时间”“回一句”两下的托盘作答的第一下第一排并进标题,第二排升上来,矮一排,数字减一0.40 秒
轻点小签上的“×”点过第一下的托盘第一下的逆逆序0.30 秒
轻点“撤回”回执、“已收到”的行作答的逆逆序、更快0.40 秒
轻点托盘(按钮以外)托盘、回执、“已收到”的行点击 → 展开详情外扩 8 px → 盒子长到整屏 → 内容淡入0.48 秒;收回 0.40 秒
长按 0.45 秒托盘、回执、“已收到”的行、英雄数字悬停 → 概要卡外扩 8 px,其余降到七成,概要卡贴下沿(放不下贴上沿)0.15 秒;收 0.12 秒
左滑过 80 px“已收到”的行知道了(托盘专属)跟手;松手从当前位置收走,计数减一;不过线 0.20 秒弹回跟手
下拉(内容在顶)详情Esc → 收回盒子跟手缩回托盘,过四分之一屏或够快就收回,否则弹回跟手;剩余按比例
左缘右滑详情Esc → 收回Safari 画返回,本页直接到终态系统
下拉、右滑托盘列不绑定只有 Safari 的回弹—
Tab / Enter、空格 / Esc托盘与行悬停 / 展开 / 收回同上;收回后焦点回到托盘—
← / → / 空格页面换轮 / 连播第 1 拍;连播每 5.6 秒一轮—
任意键、轻点讲一遍途中跳过直接到讲一遍的终态—

细则:

  • 长按的概要卡写 itemFacts 的同一组字段(来自哪条流、几份、本轮怎么变、本轮进了几条、开着的问题与期限),再加一行“最少 N 下:……”;子层的事项没有根层份额,只写标题与所在的上一层。长按英雄数字出“6 下 = 主水管 1 + FTPS 计时 1 + 待审 PR 2 + 开学文件 1 + Ture 发烧 1”。按住 0.15 秒起这一件底色微压暗;移动超过 10 px 或开始滚动取消;松手后同一次触摸补发的点按吞掉;卡出着时点它或这一件,从这一件长出详情,点别处收起。
  • 展开详情:盒子从托盘的位置长到整屏(圆角 18 → 0),题目作为锚移到顶部;详情写为什么在这里(档位、份额、期限、问题开了多久)、“这一问要几下”(这一问的路径与全页口径)、份额 · 逐轮与本轮进了它(ix.mjs 的两节,只给根层事项)、依据,底部是与托盘同样的作答控件。收回:内容淡出,盒子缩回原位,外扩收回。非嵌入页打开详情时 pushState,左缘右滑就是收回。
  • 点外:详情或概要卡开着时,点托盘列以外(桌面上是窄栏以外)收回。再点:概要卡出着时再点同一件,从它长出详情;详情里点“‹”收回。
  • 连播:长按、Tab 概要与详情停住连播,收起 0.4 秒后接着播(ix 的 resume);作答停住连播且不自动接着播,免得刚按的被下一轮卷走。换轮、←/→、合页顶栏选帧立即收起概要、详情与点了一半的第一下。
  • 键盘:Tab 到托盘出概要(与长按同义);Enter 或空格打开详情(托盘上的空格归它,不是连播);Esc 依次收回第一下、概要、详情。
  • 脚本接口 window.foldTrays,照 foldHome:model、go(i)、hover / unhover / open / close / ixAt / paused / pointOf / rowOf / ixState,answer(name, actionId)、step(name)(两下的第一下)、undo(name)、waiting()(开着的问题个数)、taps()(英雄数字)、facts(name)(这一件在哪、什么样:mark 为 tray / stepped / receipt / ledger / prior)、top()、keys(),讲一遍的 tell()、skip()、telling()。

5 实现与出入(motion → direct) ​

5.1 怎样做的 ​

  • 文件。 trays.html、trays.mjs、trays.css,用形式页框(shell.mjs,帧导航、概念卡、核对数据);在手机宽度(≤ 640 px)下页框收成一条“⌂ ‹ 时刻与旁白 ›”,概念卡与帧刻度隐藏,托盘列就是整屏;桌面上是居中的 390 宽一列,只有一道细框。
  • 复用,不另造。 动画部件全部来自 d-motion.mjs(morph、makeBox、reveal、roll、ghostOf 与 D 的时间表 BEATS.turn / answer / undo / local、GROW、SHRINK、PRESS);回应与回执来自 respond.mjs(normalizeAsk、createResponseStore、responseStatus、bindResponses、nextMorning);概要卡与详情里的“份额 · 逐轮”“本轮进了它”来自 ix.mjs;“你在原处……”来自 sketches/stage.mjs 的 responseSentence。本页只新写了:几下的口径(effortOf)、托盘与回执的标记、两下托盘的第一下(step、unstep 两拍,0.40 / 0.30 秒)和讲一遍。
  • 布局即数据。 托盘的一排固定 40 px 加 6 px 间距,头部固定,所以托盘高 = 头部 + 剩下几下 × 46 px(两份数据每一轮都核过,6.2)。右栏刻度是每一排的 ::after,跟着那一排出现、消失、移动,不另有状态。
  • 一个切点读一次布局。 每一拍先量(measureAll),再按键补丁(data-key 的 keyed patch),再量一次,然后只建 transform / opacity 动画;回执的勾填实、新问题外框是不透明度;托盘长高与收矮是三块拼的盒子在动,元素本身不改高度。
  • 讲一遍按 3.5 的时间表用定时器推进,用的就是页面自己的 go、give、stepTray;composite 替你按下的几条记录在讲完时一起撤回,story 按下的是真实数据里的回答,留下、可撤回;跳过时直接落到终态(composite 回到 f26 且什么都没替你答,story 到 f21 且回执在“已收到与关上”里)。

5.2 与故事的出入 ​

  • 推后的日子。 3.5 写的是“明早 / 周一 / 周三”。f26 是 8/16 00:00,下一个 09:00 还在同一天,写“明早”是错的;日子按与此刻的关系命名:同一天的早上叫“今早”,第二天叫“明早”,“周一”若与明早是同一天就顺延成“下周一”(f27 就是这样)。
  • 定时刻的选项。 2.1 写的是“建议的时刻,没有就是两小时后、明早 09:00”(照 respond.mjs 的 scheduleHTML)。在 00:00 出“两小时后 02:00”不合常理,改成:有建议时刻用建议,没有就给接下来的三个早上 09:00,只留问题截止之前的(composite 的“后刹排气定哪天?”在 8/13 00:00 给出今早、明早、周六)。
  • 换轮时字一律在切点换。 3.2 写了切点硬切,实现里第一版沿用 D 的 morph,期限与日期在 0.56 秒交叉淡入,和记录不符;改成换轮时每个部件都在切点换成新字(keep 全部),只有盒子与位置在动。
  • 下沉的回执一路变形。 D 让卡在下沉的前半段保持卡的样子、后半段才变成行(leadFace);托盘的回执和“已收到”的行本来就像,前半段保持回执宽度时,新的一行字会伸出到右栏刻度上,所以盒子随下沉一路从回执变成行。
  • 大数字旁的字跟数字同一刻翻。 第一版里“M 个问题”在切点就换了,大数字要到 0.20 秒才翻,中间一瞬是“6 下答完 4 个问题”。现在旁边一行在大数字翻的那一帧硬翻;下面的拆法先淡出旧句(0.10 秒)再淡入新句(0.20 秒),两句不叠在一起(叠化时两段不同的句子会糊成一片)。
  • 新问题的朱红外框。 第一版把描框的延迟写在列表上,之后每一拍改这个延迟,已经描过的外框会再亮一次;改成写在每个托盘上、描完就去掉这一类。外框从内阴影改成 1.5 px 的边框(同样只动不透明度,画起来更省)。
  • 长按大数字的概要原想把全页口径整段放进去,太长,一瞥读不完;只留拆法与一句“托盘几排就是几下:确认、批准、选择 1 下,推后、定时刻 2 下”,整段口径在页脚与详情里。
  • 讲一遍中的轻点也只用来跳过:它的点按不会再落到托盘的按钮上替你作答(键盘一直如此)。
  • 没有做的。 金额没有本页的数字键盘(只有预填与建议金额,2.1);“挑日子”“自定义”用浏览器自带的日期控件;桌面上没有悬停出概要(这是手机页面,鼠标按住 0.45 秒同样出概要)。

6 交付(ship) ​

6.1 性能 ​

无头 Chrome(打开 GPU),手机 390 × 844、3 倍像素、触屏,composite 数据;每一拍从触发起量 requestAnimationFrame 间隔,sync 是切点上那一次同步工作(补丁、量位置、建动画)。下表是 CPU 4 倍降速下的一次(三次之间切点长帧在 50–233 ms 之间浮动,其余不变):

节拍sync ms帧数fpsp95 ms最长一帧 ms
换轮 f24 → f25238161.816.716.8
叠起 f25 → f26(两个新问题进来)678060.916.883(切点)
按下一下(批准)465662.116.850(切点)
两下的第一下(推到…)404462.216.750(切点)
第二下(挑一天)235762.816.716.8
撤回214563.516.716.8
让位 f26 → f27(作答后)438061.316.850(切点)
换回 f26348161.716.833(切点)
轻点托盘展开详情734859.616.8100(切点)
收回详情74462.216.716.8
长按出概要卡162662.616.833(切点)
收起概要卡22663.116.816.8
讲一遍 composite(7.7 秒)—46059.716.850(第一个切点)
讲一遍 story(6.3 秒)—37659.516.867(第一个切点)

不降速时每一拍都是 61–63 fps,没有一帧超过 16.8 ms,sync 0–12 ms。和 D 一样,唯一的长帧在切点上,之后全程 16.7 ms 一帧。

一处没完全弄清:新问题外框还是内阴影的时候,4 倍降速下 f25 → f26 的第一次落地有 3 / 5 次在拍中(约 0.66 秒,新托盘揭开的那一刻)出现一个 150–700 ms 的长帧,脚本剖析里这段没有脚本在跑,回到 f26 的第二次落地从来没有;外框改成边框之后连量三次都没再出现。也可能是那时同一台机器上别的检查在跑,或是新字形第一次光栅化,真机上要再看(6.3)。

6.2 检查 ​

  • node scripts/check-ix.mjs --only trays:composite 29 项、story 27 项全过(story 没有两下的托盘,那三项换成一项“每个托盘一排,几下等于几个”)。项目:带 ?frame= 落地不讲、不动;托盘高 = 头部 + 剩下几下 × 46 px、大数字 = 各托盘几下之和;长按出概要、点空白收起、移动取消、点概要卡长出详情、Esc 收回且焦点回到托盘;轻点托盘长出详情、短下拉弹回、长下拉收回、‹ 收回;作答收成回执且数字减一、焦点到撤回、手指保护、撤回;两下的第一下矮一排、× 退回、第二下收成回执;连播中长按停住、→ 立即收起并换轮;Tab 出概要、Enter 打开、Esc 收回;换轮后回执落到“已收到与关上”、左滑 50 px 弹回、过 80 px 知道了;直接打开讲一遍、轻点跳到终态;减弱动态时不讲。
  • 全部页面两套数据也各跑了一遍:都过。第一遍里 C(composite)、原版树图与回应托盘(story)各有一两项计时类的没过(同一台机器上别的检查在同时跑),单独重跑都过。
  • 逐帧扫过两份数据的每一轮(composite 29 轮、story 37 轮):每一轮每个托盘的高都只有 107 px(一排)或 153 px(两排),合计等于大数字;在几个最忙的轮次把每个问题都答掉(一下、两下的推后与定时刻、要写一句的、金额、照片),数字都归零、没有报错。
  • node scripts/shoot-forms.mjs --only trays,all --gpu --check all-sync:两套数据都过,合页里回应托盘跟着顶栏的帧走。npm run site:build 通过,体验站预览图 site/previews/trays.webp 照工作环境说明的参数截(f26,收起概念卡)。
  • 截图(只在本机):f26 第一眼、按下“批准”后的回执、两下托盘点了第一下、f26 → f27 让位途中、落定后的 f27、长按概要卡、展开的详情,均为 390 × 844。

6.3 只能在真机上核的 ​

  • Safari iOS 上各拍是否同样只在切点有长帧(Web Inspector 时间线),以及 6.1 那个第一次落地的长帧会不会出现。
  • 长按:-webkit-touch-callout: none 之后会不会仍选字或出放大镜;松手后补发的点按(按会补发处理,150 ms 内吞掉)。
  • 托盘列是页内滚动区:到顶下拉时 Safari 的回弹与详情的下拉收回是否打架;左缘右滑收回详情(pushState)在真机上的手感。
  • “挑日子”“自定义”在 iOS 上弹出系统的日期滚轮,盖住托盘时的样子。