如何制作一个媲美原生体验的 Picker 选择器组件?

发表于 2年以前  | 总阅读数:767 次

hi 大家好, 在最近的一年里我主要在做 antd-mobile 5.0 版本的开发,这是一个移动端组件库。今天,我们来聊一聊其中的一个组件——选择器 Picker,它的实现思路和技术选型。

这个小分享大概会包括以下几个部分:API 设计、手势和动画、性能、无障碍。

API 设计

让我们从 API 设计开始,先来定义一下 Picker 组件的属性。Picker 是一个数据录入型的组件,所以它需要 value onConfirm 这两个属性来和外界进行数据的交互。然后它还需要一个 columns 属性,用来配置 Picker 中每一列的选项列表。这里我们省略了一些属性,只保留了这几个最关键的。

需要注意的是,columns 属性是一个二维的数组,第一级对应的是选项列,第二级对应的是每一列中的选项列表。

定义好属性之后,我们来拆解一个这个组件的元素构成。

首先呢,我们在这里先放一个 Popup 组件,这是 antd-mobile 中的一个基础布局组件,我们可以直接用它来作为弹层的容器。然后我们往里面填充一些内容:我们在头部放两个操作按钮;在主内容区,根据 columns 属性渲染出对应的选项列表,也就是“拨轮”。拨轮的本质就是一个长长的列表,我们会通过 CSS 的 transform: translate 让它进行上下的偏移,并且把溢出的部分隐藏起来,这样看起来就像是一个轮盘在被转动了。

最后,我们来增加一个遮罩层,在拨轮的上半部分和下半部分都盖上一层渐变的白色遮罩,这样用户能就看清当前选中的是哪一项了。 由于拨轮这块的逻辑会比较复杂,所以我们可以把它独立拆一个组件出来。这样我们往下的讨论也可以更聚焦一些。 Wheel 只有三个属性:column 选项列表;value 当前选中的值;onSelect 当用户进行选择时会触发的事件。

⼿势 & 动画

接下来呢,我们为它增加一些交互。在用户对 Picker 进行操作时,有两种可能的方式。第一种是直接点击选项,另外一种是通过手势拖拽。

为了更好的描述我们如何去实现手势和动画,这里我们把这个问题抽象成一个最简化的版本。这里有一个小方块,我们可以按住鼠标,把它拖拽起来,当我们松开鼠标的时候,小方块会落回原位。

那这里首先呢是需要实现手势的处理,手势的本质就是鼠标或者是手指在 x 轴、y 轴方向上的移动距离。在 React 的生态社区之中,有很多的手势库,我们选择了 use-gesture。我们可以使用它提供的 useDrag hook 来定义一个手势,当触发了拖拽事件时,我们将滑块的 y 轴位移 translateY 设置为当前手势的移动量。

接下来,我们需要在拖拽结束的时候,让小球落回原位,这里就涉及到了动画。

相信大家提到动画,首先都会想到 CSS transition,因为它实在是太简单直接了。通过这行 CSS 代码,我们可以让滑块的 transform 属性(也就是它的位移)自动的实现动画过渡。但是我们其实只希望它在回落的时候有这个动画效果,而在拖拽中滑块应该是立即跟随鼠标位置的。所以这里我们在手势中增加一个判断,如果用户还正在进行拖拽,那我们给它增加一个 grabbing 的 class name,从而禁用掉 transition。当手势结束时,我们移除掉 grabbing 这个 class name,并且将它的位置设置回原位。这样就回出现一个回落的动画效果。

来看一下效果,似乎还是不错的。但是,我们需要再考虑一种边界情况。如果我们在这个滑块正在下落的过程中,再次点击了它,那么我们应该需要实现的是能够在半空中“接住”这个滑块,并且继续拖动它。

而事实上现在的效果是,我们一点这个滑块,它就会重新从原点开始被拖拽。这是因为,对于 CSS 动画来说,我们很难获取到它的动画进行到了哪个状态。如果我们希望得到在鼠标按下时滑块当前的位置,那可能就需要通过一些比较特殊的方法来实现了,比如说 getBoundingRect。所以,CSS 动画在某些场景下的确简单好用,但是当我们需要对动画状态做很多的干预和处理的时候,我们很容易会遇到一些它的短板。

所以呢,我们需要换一种方案,也就是基于 js 的动画。

让我们以 react-spring 为例,它是一个有点像物理引擎的动画库。 首先,我们使用它提供的 useSpring 创建出一个弹簧动画,动画的初始位置是 y 等于 0。然后我们通过 animated.div 这样一个特殊的元素,把刚刚得到的动画量 y 和 slider 的位移量绑定在一起。接下来我们对手势的处理逻辑进行一下调整,当手势结束时,我们把 y 重置为 0。而如果手势还在拖拽中,我们把 y 立即更新为当前的手势偏移量。然后这里需要特别注意的是,我们在手势的配置中增加了一个 from 参数,它表示的是每一次手势开始的时候,初始的偏移量是多少。而大家可以看到,这里我们给它设置的初始偏移量是 y 这个动画变量的当前值。也就是说,我们会让手势从动画当前进行到位置继续,而不是强制它从原点开始。

现在,我们就可以拥有完美的手势体验了。基于同样的思路,我们可以给 Picker 也增加上手势和动画效果,当然会更复杂一些,但是思路是相通的。

说完手势和动画,我们再来考虑另一个问题。当我们在使用选择器时,很多个拨轮之间可能是有联动关系的。比如省市区的选择。但是就目前的数据结构而言,Picker 组件的多个拨轮之间是彼此独立的。所以我们需要能够在第一列变化时动态调整第二列的选项列表。

OK,那我们来改一下 columns 属性,让它允许用户传入函数动态生成 columns。然后我们基于 Picker 的这个能力,封装出一个 CascadePicker。

所以我们现在有了两个组件:一个通用的 Picker 和一个专用于级联场景的 CascadePicker。他们的属性几乎是完全一样的,但是 Picker 的选项是通过 columns 配置的,columns 是一个二维的数组。而 CascadePicker 的选项是通过 options 配置的。和 columns 不同的是,它是一个树形的结构,更准确的说,是多个树形节点组成的数组。

大家可以看到,这两个组件,有 16 个完全相同的属性,却只有一个不同的属性

那为什么不把这两个组件合二为一呢?就像这样: 我们可以让 columns 属性同时支持两种类型的数据格式,然后在 Picker 组件中去做判断,看用户当前的数据是不是级联模式,然后用对应的逻辑去进行处理。听起来也不难实现。但是这种方案也存在着一些弊端:

首先,左右两种数据结构看起来只是增加了一个可选的 children 属性,但是数据类型已经不一致了,左边是一个两级的数组,而右边是一个一级的数组。如果不特别注意,非常容易搞混和写错。

第二点就是 columns 属性已经不再名副其实了,如果说 columns 是一个数组,代表的是很多列选项,那这个数组其中的每一个元素应该是一列选项才对,就像左边这样,第一列是省,第二列是市。而对于右边这种级联的数据结构,数组中的第一个元素是浙江节点,第二个元素是江苏节点,我们能说浙江是一列、江苏是另一列么?显然是不可以的。如果我们给这种结构的数据起个名字,那也应该叫做 tree、treeNodes 或者是 options。第三点,是这样做会导致很多逻辑被塞在 Picker 组件中,而过度的逻辑耦合可能会对组件的后续改动造成一些限制。假如说我们想给 Picker 组件增加一个新属性,那就要同时考虑级联和非级联的两种情况。而如果我们希望做一些内部实现上的优化,复杂的参数结构也可能会带来额外的成本。

我们的原则是,可以选择重复,但不应该去选择错误的抽象。优秀的代码结构和 API 设计不应该以组件数量、属性数量来进行评判。如果我们不去谨慎地处理抽象,仅仅是依靠外在的“形似”来做聚合,那反而可能会导致代码越来越难以维护。

基于类似的思路,我们还可以制作日期选择器组件。它最核心的部分也是这个 columns 的生成函数。

性能

我们来试一下:感觉还是很流畅的。不过,如果我们把 CPU 的性能做一些限制,让它变慢 6 倍,就会发现有一点点的卡顿,在 Chrome 的性能面板中,我们可以很明显的观察到有几帧丢失了。 那我们来排查一下,这里我们用到了 React dev tools 的 profiler 工具,我们对刚刚这个操作进行一下录制和分析,会发现 Wheel 组件被频繁的渲染了。 如果我们把被渲染的组件高亮一下,就会发现,当滑动第三个拨轮的时候,可以看到五个拨轮全部都被重新渲染了,这显然是不符合预期的。

解决方案自然是给 Wheel 加个 memo,但是这里直接套一层 memo 是不会有效果的,我们还需要做一些特殊的处理,包括调整 Wheel 的属性、自定义 props 的对比逻辑。因为今天时间有限,这些我们就不具体展开了,感兴趣的朋友可以在 GitHub 上阅读 antd-mobile 的源代码。

OK,经过这波优化之后,我们再来测试一下效果,可以看到转动第三个拨轮,只会触发它自己的重新渲染。React Profiler 中也可以看到组件的渲染有明显的减少。丢帧的情况也不存在了。

⽆障碍

大家可能会觉得,现在这个 Picker 组件总该算是做的差不多了吧?很遗憾,并不是。

在我们的身边,有很多的失明人士,根据 2018 年的统计,在中国,每 80 个人中就有一位视力残疾患者。他们没有办法清楚地看到屏幕上显示的文字和按钮,那对于他们来说,想要操作手机,就需要借助于屏幕阅读器。大家可以想象出来,一位盲人,该如何使用这个 Picker 组件吗?

我们来直接试验一下:

相信这个体验不用多说,正常人都没办法忍受,那,又该怎么优化呢?

作为开发者,我们大多数人应该都对无障碍这件事情挺头大的,特别是对于 Picker 这种看起来交互就很复杂的组件。无障碍能力本身并不复杂,通过屏幕阅读器,我们能做的操作就是非常简单的上一项、下一项、点击一下当前项,这本身再简单不过了。但是我们的网页是在一个二维平面上呈现的,而屏幕阅读器却只能把它识别为一个一维的线性的信息结构,这之间的差异,才是导致无障碍能力实现困难的根源。我们尝试了很多办法,最终,选择了去为屏幕阅读器单独实现一套交互。 首先,我们让下面的这层拨轮被屏幕阅读器忽略掉,然后我们再往上面叠加一层内容,包括一段文字,表示当前这一列选择了哪个选项,以及两个操作按钮,用来选择上一项和选择下一项。最后,我们把上面这一层内容设置为完全透明,让我们来看一下实际效果。

说了这么多,我们已经差不多要结束了,但在最后,我想替整个 antd-mobile 的社区,以及所有 antd-mobile v5 的使用者感谢参与过 v5 版本建设的同学,从 21 年 7 月推送到 GitHub 开始,到年底为止,在这短短半年的时间里,我们收到了来自 50 位社区同学的 325 个 PR , 460 个 commit 和 416 个 issue。不论是参与代码的编写,还是反馈问题或者是参与讨论,这些都对 antd-mobile 的建设都有着非常非常重要的意义,感谢大家对 antd-mobile 的支持。

人们常说,“一个人可以走的更快,但一群人可以走的更远”,我想或许这也是对开源精神的最佳诠释吧。

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/MPSTJ1X_5yHd-JL25X0pSA

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237227次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8063次阅读
 目录