wheel-picker-cj 源码拆解双 Scroll 同步实现滚轮吸附的底层原理【免费下载链接】wheel-picker-cj滚轮选择UI组件项目地址: https://gitcode.com/Cangjie-TPC/wheel-picker-cjwheel-picker-cj 是一款基于仓颉Cangjie语言开发的滚轮选择器 UI 组件库一行import即可在应用中拉起仿系统级的滚轮选择时间选择器、省市区三级联动、年/月/日选择器和任意自定义数据滚轮。今天不聊用法直接拆开它的核心组件 wheel_picker_component.cj回答一个问题滚轮为什么能吸附到最近的数据项选中项为什么能单独高亮答案藏在两个叠在一起、互相跟单的Scroll里。先看看它的最终效果自定义滚轮支持循环滚动、指示线、幕布配色等效果官方示例页入口在 entry/src/main/cangjie/pages/ 下核心布局一个 Stack 里叠两个 Scroll打开 wheel_picker_component.cj 的build()方法每列滚轮的结构是一个Stack里面按顺序叠了四层层级组件高度职责1️⃣ 底层Scroll(scroller)轮高可见项数 × 单项高真正的数据列用户只操作它2️⃣ 中层Divider× 2线宽选中区域的上下指示线3️⃣ 上层Scroll(scroller1)只有 1 个数据项高幕布只显示选中项并染上高亮色4️⃣ 顶层Flex渐变层可选轮高空气感氛围效果关键就在第 3 层wheel_picker_component.cj 中的scroller1只有一项那么高、enabled(false)禁止交互内容却是同一份数据、但文字颜色换成mSelectedItemTextColor选中色。它永远盖在正中间所以中间那一行永远是高亮色上下看得到摸不着的数据项则显示为灰色。双 Scroll 的同步公式底层Scroll每滚动一帧onScroll回调就把上层幕布拽到同一位置wheel_picker_component.cj非循环模式scroller1.scrollTo(y mOffset)两个偏移量完全一致循环模式scroller1.scrollTo(y mOffset 半屏项数 × 单项高)因为循环模式下数据首尾各垫了一层空项occupyText()幕布需要补偿这个差值才能保证盖住真正的选中项。组件初始化时aboutToAppearwheel_picker_component.cj同样走一遍先把底层滚到初始选中位再把幕布对齐——这就是打开页面时首选项已高亮的原因。滚轮吸附一次取模运算松手自动对齐最近一项是滚轮选择器最核心的手感全部逻辑在onScrollEnd里wheel_picker_component.cj取余数scrollOffset 当前偏移 % 单项高度得到偏离对齐线多少像素余数 半格 → 向上补一格yOffset - scrollOffset 单项高余数 半格 → 向下回补yOffset - scrollOffset用scrollTo带duration默认 100msCurve.Ease缓动完成最后一小段位移。顺带一提仓颉的Float64本身没有取模运算符这个%是项目自己在 package.cj 里用this - trunc(this / x) * x扩展出来的——这是读这套源码前需要知道的隐藏依赖。为什么还需要一个 scrollSwitch 开关onScrollEnd对所有滚动结束都会触发包括组件自己调用scrollTo引起的滚动。如果不对自己发起的吸附动效再次吸附就会无限循环。所以组件用一个scrollSwitch布尔开关手指抬起TouchType.Up才置true吸附执行一次后立即置回false程序化滚动就不会再触发吸附。这是一个很典型的防自激设计。循环模式数据复制 边缘瞬移循环滚轮没有无限列表用的是更朴素的技巧复制数据wheel_picker_component.cj 的scrollIndex()里循环模式下把整份数据重复渲染 3 遍数据量不足可见项时会自动增加倍数让滚轮怎么滚都滚不穿边缘瞬移onScrollEdgewheel_picker_component.cj检测到到达Edge.Top/Edge.Bottom时以duration: 0直接跳到另一段副本的对应位置视觉上就形成了无缝循环。尺寸体系为什么可见项数必须是奇数模型类 wheel_picker_model.cj 的updateVisibleItemCount()里藏着一组推导单项高 文本高 2 × 项间距 轮高 可见项数 × 单项高 半屏项数 (可见项数 - 1) / 2如果可见项数是偶数会自动 1。原因很直白只有奇数个可见项选中项才能正好居中上下各留出对称的半屏项数行非循环模式下首尾的空白占位项occupyText也正好各用半屏项数个保证第一项、最后一项都能被滚到中间被选中。从单列到多列日期与地区选择器的组装方式理解了单列复合选择器就是复制粘贴 联动日期选择器wheel_date_picker_component.cj年/月/日三列共 6 个Scroller3 个数据 3 个幕布。联动的精髓在月列的onScrollEnd里月一变先检查新月份的最大天数若当前日越界就自动钳位wheel_date_picker_component.cj再拼出年-月-日字符串。年份区间数据由 solar_calendar.cj 生成地区选择器wheel_area_picker_component.cj省→市→区三级联动省列滚停后刷新市列数据市列滚停后刷新区列数据并通过OnAreaSelectedListener逐级回调。数据来自内置的 province.cj 和汇总常量 constant.cj事件出口两个监听器接口定义在 listener.cj。单列滚轮则更简单——吸附完成后模型会调用你在setSelectDataShow里注册的闭包把选中文字直接交给你实现滚轮静止即可拿到选中值。快速上手三步接入引入模块把wheelpicker拷入工程在entry的oh-package.json5和cjpm.toml中声明依赖步骤详见 README.md 的使用说明章节配置模型WheelPickerModel()提供链式 BuildersetData、setVisibleItemCount、setCyclic、setIndicatorColor等任意组合完整 API 见 doc/feature_api.md挂载组件WheelPicker(model: this.myModel)一行搞定。想让滚轮程序化选中某一项调用setSelectedItemPosition(index)即可它会带缓动地滚到目标位置wheel_picker_model.cj自定义选择器示例页里那个go to按钮就是用它实现的。小结滚轮选择器看似玄学的两件事拆开其实都很克制选中高亮 双层Scroll叠放 onScroll逐帧同步幕布层只露出一项高亮文本松手吸附偏移 % 单项高取余过半向上补、不足过半回退一次带缓动的scrollTo收尾无缝循环 数据渲染 3 份 触边duration: 0瞬移。如果你想深入某个细节比如幕布在循环模式下的偏移补偿、日期联动的越界钳位建议直接读 wheel_picker_component.cj 和 wheel_picker_model.cj 这两个文件配合本文的表格对照阅读基本半天就能吃透整条滚轮链路。【免费下载链接】wheel-picker-cj滚轮选择UI组件项目地址: https://gitcode.com/Cangjie-TPC/wheel-picker-cj创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
