【山竹记账】日期选择组件弹性动画分析

前言:一个日期滚轮,为什么不只是修改 translateY?

源码

移动端的日期选择器通常由年、月、日三列组成。用户上下拖动某一列,数字跟随手指移动;拖过首项或末项时,列表还能被稍微拉出去,但越拉越费力;松手后,列表又弹回最近的合法选项。

看上去只是几个数字在移动,实际上需要同时处理四件事:

  1. 跟手:正常范围内,手指移动多少,列表就移动多少。
  2. 越界阻尼:超过首尾边界后,不突然卡住,也不无限移动。
  3. 离散吸附:松手时,选中项必须对齐到一整行,不能停在两项中间。
  4. 弹性过渡:从松手位置到目标项之间,有自然的回弹过程,而且可以被下一次手势打断。

本文结合山竹记账中的以下文件,拆解这套交互的实现:

文件 职责
src/components/form/DatePickerPopup.tsx 组织年月日三列、生成选项、绘制遮罩和选中框
src/components/form/DateTimeColumn.tsx 处理触摸生命周期、驱动动画、同步选中值
src/components/form/dateTimeColumnMotion.ts 提供拖动、阻尼、索引计算三个纯函数
src/components/form/__test__/dateTimeColumnMotion.test.ts 用 Vitest 验证核心数值规则

动画使用 @react-spring/web,当前项目中的版本是 10.1.2。本文代码与分析以当前实现为准,不把后文讨论的改进方向描述成已经完成的功能。

先给出全文最重要的结论:

拖动时的“阻力感”来自位移映射,松手后的“回弹感”来自弹簧动画。前者解决“显示在哪里”,后者解决“如何随时间到达目标”。两者不是同一件事。

【山竹记账前端-react】14.无限加载列表

【山竹记账前端-react】14.无限加载列表 大纲链接 § [toc] 1. 无限加载的实现思路 ⇧ 访问 /items 页面,请求 /items?page=1 每次加载10项数据,返回 { resources: [], pager: {count: 10, per_page: 10, page:

【山竹记账前端-react】13.滑动菜单暗藏玄机

【山竹记账前端-react】13.滑动菜单暗藏玄机 大纲链接 § [toc] 1. 滑动菜单之样式制作 ⇧ @/components/FloatingOverlayMenu.tsx 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { MenuList } from "@/components/menu/MenuList" import { UserHeader }