下午两点十三分,一条提交把滚轮退回去了。十五分钟以后,另一条提交把滚轮重写了回来。

两个多钟头里,同一个选择器被加上、修了三轮、退回、重写。TxPicker 那一天的提交,我是按时间戳一条条排下来看的。

12:19

TuffEx 是 Tuff 自己的 UI 库(这个名字是 2025 年 12 月前后才有的,之前叫 tuff-ui),现在有 194 个组件目录。TxPicker 是里面的选择器,一列一列的行;日期选择器里的年份那一列,从 1970 排到 2100,一共 131 行。

12 点 19 分,一条提交把每一列做成了 3D 的滚筒。它给的动机很朴素:原来的列就是一个平的列表,上面压一条高亮条,和一个短的可滚动列表分不出区别。

做法是让原生的滚动条留在下面,滚筒涂在上面。说明里特意强调,这是有意的:平台自己的惯性和减速、吸附、键盘操作、listbox 的语义,全都还在。

三轮修补

三轮挨得很近,差不多隔十分钟一轮,只是第一轮离 12 点 19 分,已经隔了一个多钟头。

13 点 45 分,修几何。行本来是平铺着一行接一行排下来的,每一行有自己的起点,原地一转,除了中间那行,其余的都被甩出了自己的位置,也甩出了视野,整个滚轮只剩一个大大的行,和下一行的一个影子。

修法是先把排版的偏移抵消,再转上去:行高 36 像素、每行转 18 度,第二行落在中线下面 35.2 像素的地方,和排版时的位置只差不到一像素。

13 点 55 分,修卡顿。每一行都挂着 will-change: transform,等于每一行向合成器要一个图层,演示里七行不算什么,可年份那一列有 131 行,一个滚轮就是 131 个图层。这一条把它去掉了,还把写滚动偏移的动作挪进了每帧的回调里。末尾还留了一句:长的列仍然把每一行都画出来,年份那一列要是还不够顺,下一步是只画看得见的。

14 点 04 分,修滚不到底。列的上下各有半列空白,好让第一行和最后一行能走到中线,这半列原来是滚动容器的 padding,有的环境里不算在可滚动的范围内,于是最后一个选项看得见,却永远拨不到中线上,也就永远选不上。

14:13

14 点 13 分,这一条把滚筒退回成了平的列。从加上去到退回来,一小时五十四分钟。

理由我转述一下。每一行的点击范围会跟着它自己的变形走,中间那一行被透视推向观察者,又被放大,把邻居盖住了;点旁边的一行,落下去的是中间那行(它本来就在中间),于是滚轮什么反应也没有。提交里的说法是,毛病长在做法本身上:把滚轮画在一排行上,而这些行还得在排版时的位置上被点中。

前面三轮,每一轮对着的都是真毛病,却没有一轮碰到这里。我觉得这种事挺难察觉:一直有毛病被修掉,看着像在往前走,其实一直没修到点子上。

退回的也不是整个下午的改动。有两处修复被特意留在了退回之外,提交里说它们跟滚筒本来就没关系:一处是 14 点 04 分修的最后一行,那个毛病在滚筒之前就有,只是列里的行比窗口显示的多才露出来;另一处是 scrollToIndex 在没有 scrollTo 的环境里的兜底。

提交里接着写了另一种做法:用 transform 驱动整列,手势、惯性、吸附全部自己来,像手机上那种选择器,不再涂在原生滚动条上面。12 点 19 分那条把原生滚动条留在下面当成重点,到这里成了要换掉的东西,我读下来是同一个选择:前一条拿它当优点,后一条拿它当毛病的根。它又说,这是一次重写,在没法用浏览器操作它的情况下,不该落地。

14:28

十五分钟以后,重写落地了。标题大意是,把选择器重做成一个自己会转的滚筒。

位置只是一个数,以行为单位的偏移,转的时候带小数。行都先堆在中线上,再从那里摆到滚筒上,所以转起来没有排版的偏移跟它较劲,13 点 45 分那一轮要先抵消的东西,这里根本不存在了。

行也不再参与命中测试。点中了哪一行,由点击的坐标用 asin(dy / radius) 反推出来,说明里举的例子是,画在中线下面 35.2 像素的那一行,反推回来正好是第 1 行。35.2 这个数,13 点 45 分那条里也出现过。

拖动、松手后的滑行、停靠,也都是自己写的,停靠用的是三次方的缓出,说明里的形容是离开得快,到达得慢。只画四分之一圈以内的行,不管列多长,默认就是 11 行,年份那一列的 131 行正需要这个,13 点 55 分那句留着的下一步,到这里已经做了。

最后三条说明,各自交代了验证到哪一步。14 点 04 分那条去掉了前一条加的 contain,说它对滚动容器自己的可滚动范围有什么影响,当时没法核对;14 点 13 分说重写不该在没法用浏览器操作的情况下落地;14 点 28 分落地了,末尾写的是几何和点击的反推用数值和测试核对过,惯性的手感 jsdom 判断不了,浏览器里还没验证。

中间那十五分钟里发生了什么,能看到的只有这个顺序。

后来 TxPicker.vue 又被改过两次,标题都不是回退:九月底那次,在一条大提交的明细里,滚筒的半径、透视和窗口改成了跟着可见行数走,点击的反推也从 asin(dy / radius) 换成了带透视的算式;十月中旬那次,只动了字重变量和高对比度下的样式。惯性到底顺不顺,我在记录里没找到答案。