这次分享,旨在让产品、设计同学能意识到,从原型图、设计稿到实际落地,还差的东西。
一、当第一眼看到 Send 这堆设计稿,前端同学的第一反应是什么?
我的第一反应(还没细看具体内容时候),我首先意识到是这是一个拥有动态高度的多步 Modal。
动态高度不止体现在每一步之间有差异;同一步也会存在不同的状态,对于不同的高度。
二、前端同学们觉得,这种设计会产生什么问题吗?
-
以第一步而言,起码会有 3 种不同的高度,会随着用户的输入跳变。
- 显示 Tabs 列表
- 校验通过的合法地址
- 校验未通过的地址和未通过 Tip(根据未通过文案的高度会再衍生出 n 行的差异)
-
这种高度的跳变是直接作用在用户视觉中心上的,会随着 input 输入而变化,连 Next 按钮的位置都会发生巨大偏移。
-
接着想到:如果不把 Modal 绝对垂直居中,而是距离顶部一个固定距离,是否可以减轻一些跳变带来的 UX 影响?(打个问号?)
三、接着回想一下其他钱包怎么做的,这个问题为什么会出现在 Mega Wallet 上
-
其他钱包多是以插件、mobile 的小屏形态,Send 页面直接铺满整页。
-
我猜之所以 Send 做成 Modal,是因为这是大屏的 desktop 钱包:出个 Modal 而不是铺满屏幕,在视觉上更符合一个大屏应用的直觉。
但是设计出这个 Modal 的时候,请问产品和设计师是不是没有考虑过会对交互造成什么影响?
四、细看 Send 内容
第一眼右上角 Account 1,然后去看了后面几步,确定只有这个地方标识了「当前账户」,并且只显示了个别名。
- 没见过有钱包在签发环境不显示发送账户地址、当前网络这俩个信息。
- 除非这个钱包是单链钱包,比如只支持 Mega 主网,测试网都不支持的这种。
- 看起来像个按钮,以后有可能会在这里可以点选切换
currentAccount吗?
五、输入框职责问题:Tabs 和校验,哪个优先?
意识到第二节第 1 点里提到的,根据一个输入框分情况显示 Tabs 列表、校验通过、校验未通过 + Tip,在交互上有一个坑。
先来看看其他钱包咋做的,为什么没有问题
- OKEX:整页铺满,上面是 Input + 校验结果,下面是 Tabs。两者各自运作完全独立。
- MetaMask:Input 可以单独点进去打开 Contact Tabs,外面只管验证。
请问按我们的设计稿
输入框的第一职责是 match tabs content,还是校验输入内容?Tabs 和校验,哪个优先?
三个方向分析
| 方向 | 策略 | 缺点 |
|---|---|---|
| 方向一 | 视为平级,首先去模糊匹配 Tabs,匹配不到就开始校验 | 哪怕是比较好的处理了逐位输入、连续输入、复制粘贴情况,也难以避免会出现 Modal 高度的频繁抖动 |
| 方向二 | 校验优先:Tabs 只用来在进来时候点选,只要用户开始在 input 里连续输入,立马开始隐藏 | 失去模糊匹配 List 能力。开始输入跳一次高度 |
| 方向三 | Tabs 优先:input 首先用来模糊匹配 Tabs,只在特定情况开始校验 | 需要理清、明确特定情况。输入正确跳一次高度 |
明确目标
不让这个 Modal 因为你每输入一个字符就「抖」,保留「从 Recents / Contacts / My Wallet 里快速搜人」的能力,并且在你输入完整后给出明确的校验/解析过程与结果(可用 / 不可用 / 解析中)。
选择方向三,并做出如下规则
1. 还没输入 / 正在输入时
下面一直是可选列表(Recents / Contacts / My Wallet),并且会跟着你的输入做筛选。
-
软提示:当你从连续输入中停止(这里的「停止」不是主观感觉,而是实现上约 600ms 没有继续输入,进入 settled 状态),如果判断你输入的内容已经「像在输地址」但还没输完(以 EVM 为例:
0x开头但还没到 42 个字符,也就是还没凑齐0x+ 40 位 hex),输入框会先淡红边提醒你「可能在输地址」;但下面列表仍然保持,你可以继续搜/点选。- 反过来,如果你输入的不是「地址那种样子」(以 EVM 为例:既不是
0x开头,也不是完整的 40 位 hex),此时默认是在用它做模糊匹配筛选列表,所以不会触发这条淡红边提示。
- 反过来,如果你输入的不是「地址那种样子」(以 EVM 为例:既不是
-
同时:这个阶段系统也在默默检查你的输入(输入变化触发校验本身有 ~300ms 的 debounce,会稍微等一等并随着你继续输入不断更新;并且校验结果会缓存一段时间(约 5min stale),所以重复校验同一段输入可能会显得「秒出」),但不会用占高度的大提示去「抢屏」;大提示只在你输入完整后出现。
-
为了进一步压住 Modal 抖动:列表内容区固定显示 3 行(这也是设计稿没提的,到底该显示几行),校验提示/解析结果区固定高度 64;这样不同长度的文案不会把整个弹窗顶得忽高忽低,CTA 的相对位置也更稳定。
2. 只有当你输入「看起来已经是一整段地址/域名」时
列表才收起来,并把校验/解析的过程与结果用更明显的提示展示出来。
「看起来已经输完」的判定是按当前网络/链来的(为了多链兼容,也为了避免你输入到一半就被红字打断):
- EVM:
0x+ 40 位 hex(总 42 个字符)就算「看起来输完」;另外如果你粘贴了 40 位 hex 但忘了带0x,也会被当作「你可能输完了」来处理,从而尽快给出明确反馈 —— 当前会被判定为地址格式不合法(需要补上0x)。 - Solana:更偏「长度/字符集门槛」(通常是 32 位以上的一段地址串),不靠
0x这种前缀。
校验结果处理:
- 校验中:给 loading 提示(类似 “Fetching address info”)
- 校验失败:给明确错误提示 + 输入框标红
- 校验成功:按钮变成 “Send to this address”
- 如果是域名:会额外告诉你最终解析出来的地址
注意:域名只在支持的网络里开放;不支持的网络就只认地址
常见边界场景
-
粘贴了 40 位 hex 但没带
0x(EVM)这种常见于某些 API/表格/日志里存的地址字段(只给 20 bytes hex,不带前缀)。会直接按「你可能已经输完」处理,列表收起,提示「地址格式不对/需要补上
0x」,避免你卡在「列表没匹配但也不知道哪错了」的状态。 -
完整地址不小心删掉一位(或删掉一截)
一旦删到「明显还没输完」,列表会回来;并且输入框会自动恢复焦点(Tabs 从隐藏→显示时会延迟约 50ms 再 focus 一次),让你可以继续输入而不需要再点一下。如果你停一下且仍像在输地址,会出现淡红边提醒,但不会抢占大提示。
-
完整地址不小心多打一位 / 多粘贴一个字符
因为它仍然「像是输完了」,所以会直接进入校验并给出明确错误提示(而不是继续让你在列表里瞎找)。
六、Recipient Picker 其他的一些小问题
-
Fetching address info 设计稿上,给了个 List Skeleton。应该给个单独的「正在校验中」设计稿。
-
Bad ChainID 处:
- 首先我不明白什么场景能出这个校验错误
- 其次,我不明白为什么会把很多地址校验放在下一步 SendStates 里显现,不应该放在 Recipient Picker 更符合直觉吗?
- 而且这两处校验 Tip 是不同样子的
-
没有给出真正意义上校验错误(地址不对、在诈骗黑名单地址)这类情况的设计稿。
-
注意市面上多链钱包点击 Send,多是先选 Token、再选 Recipient(Mega 是反过来)。
- 因为 Token 里隐含了链的信息
- 而交易历史、联系人可以是钱包级也可以是账户级的
- (我不知道 bad chainId 是否和这个有关系)
七、Token Picker
黑白名单问题
- 晨阳说目前的 token list 是黑名单模式的,而黑名单模式与 New Token Found import/use 相悖
- 得明确一下,如果走白名单模式,白名单怎么去管理。import/use custom asset 资产管理,放在钱包、账户哪一级
其他问题
- 没有给出真实覆盖在 Modal 内容区域上是什么样子(trigger gap 是否合适),只给出了一个疑似 Dropdown 的宽度大于 Modal 宽度的东西
- 没有说明是把 selected token 排掉,还是给个特殊样式
八、Send Successful
-
SendStates Happy Case 下面有一句
Automatically close the modal upon completion,这个和下一阶段的 Successful Toast 不是相悖的? -
2s ago有点离谱,是不是还得整个定时器(现在a few second ago) -
没提如果 Send 失败的不同情况(上没上链怎么办)
- 个人倾向于:不上链的错误在第二步弹 Toast 方便再次点击发送
- 上链的错误加个 SendFailed 第三步
九、NFT & NFT List
(待补充)
十、Send 还可以通过其他方式打开
比如外面 NFT 列表直接点选发送,比如主动扫开一个 Receive QRCode。
三个入参都是可选、可任意组合的:
asset: AssetSchema,
recipient: z.string(),
amount: z.string(),
目前表现
- 如有
recipient直接填上,复用校验 UI,如果校验过了无需主动点击 Next 就可以进入下一步(正常流程中需要主动点) - 如有
asset在进入第二步拉取完列表后做一次匹配,如果有amount就顺手填上 - 没
asset单填amount无效
十一、设计稿的一些细节
| 组件 | 问题 |
|---|---|
| RecipientPicker input | 背景是白色的,高 40 |
| TokenPicker input | 背景是灰色的,高 44 |
| Modal 底部 Button | 高 48 |
| View All 按钮 | 高 28 |
| Button 组件定义 | small/medium/large 是 32/44/52 |
| RecipientPicker / SendStates | 顶部标题距离内容区域 16 |
| SendSuccess | 顶部标题距离内容区域 22 |
另外:出现多个不在 CommonColor 里的颜色。
个人建议
同样层级、职责的地方,大小、间距这些应该一致,有助于保持视觉上的一致性提升产品质感。
可以定一个通用组件的尺寸范式,出一个新区域的设计稿的时候,根据范式限制自己、不能太过于自由发挥。
十二、PRD 相关问题
-
GasFee/Nonce 不出现在 Send 里,好像没看到有钱包这么做,这么做的理由是什么?
-
为什么不一起把 ENS 处理了? 我理解这个要是不考虑到一起处理,后面再加容易堆成屎山代码。