本文深入剖析了购物车拖拽功能的技术实现路径,在复杂业务场景下通过精细化控制、合理架构分工与细节体验打磨,实现了高效、稳定、流畅的用户交互体验。
视图
同一店铺内商品间可以随意排序, 同一分组内的商品可以随意排序, 店铺里的商品可以放进任何一个分组, 凑单条必须紧跟着店铺头 ······
{
type: reorderGroup|reorderInGroup|joinIntoGroup|removeFromGroup // 当前操作的类型
fromBundleId: XXXXXXX // 来自哪个分组/店铺
toBundleId: XXXXXXX // 去往哪个分组/店铺
from: XXXXXXX // 操作品的cartId
to: XXXXXXX // 松手处上方的cartId
}
现实映射
车厢:一种是 VIP 号码车厢<店铺>;一种是普通车厢<分组> 车厢门:每个车厢有两个门,分布在车厢两侧<店铺头>;有铰链连接后一车厢<分隔符> 窗帘:仅 VIP 号码车厢有窗帘<凑单条>,普通车厢没有 乘客:
普通乘客<商品>:每人都有一张 VIP 卡,对应一个 VIP 车厢号 绑定乘客<搭配购商品>:乘客 A1、A2、A3。 他们不能被分开(中间不能被插入任何乘客;始终在同一车厢)
仅有一个限制:VIP A 的乘客,无法进入 VIP B 车厢
若持有 VIP A 卡号的乘客在 VIP C 和 VIP D 间停留,铁路系统会如何分配?
进入 VIP C? 进入 VIP D?
在车厢 C D 之间新建一个普通车厢✅
如果 VIP B 车厢内仅有一个乘客 B,该乘客进入了普通车厢 2,铁路系统如何分配?
保留 VIP B 车厢? 删除 VIP B 车厢、窗帘等附属节点✅
如果 VIP C 车厢里来了一位乘客站在了窗帘和门之间,该如何调节?
就放在那,形成【门、人、窗帘】的排序 帮他挪出窗帘,形成【门、窗帘、人】的排序✅
长按触发,长按组件触发拖拽逻辑,生成对应组件的缩略版,同时折叠需要隐藏的组件
拖拽移动,滑动组件,根据悬停位置和 list 底部的元素进行交换,如果触及屏幕边缘,主动滚动列表
-
松手放置,松手或者切换 tab、锁屏,意为退出本地拖拽,根据当前位置放置元素和发送网络请求
根据用户当前长按的元素,判断该元素是商品、搭配购商品、还是分组、店铺。各自路由到对应的缩略版元素上即可。
正常态 |
管理态(简化版) |
拖拽状态(缩略版) |
|
正常商品 |
|||
店铺 |
拖到可以放置位置的时候,Weex 将底层 list 里的元素腾出可以被放置的位置 在 onHover 事件中判断放置的类型,给用户相应的提示
list 中的拍平列表与视觉上的树形结构映射较为复杂(一维列表操作二维树形结构) 分组、搭配购,店铺组件折叠及互斥逻辑 非可拖拽元素的兼容处理(头部组件、信息流、分割线) 底部信息流处理(嵌套列表) 管理态度下 height = 0 的隐藏元素的参与(凑单条、筛选项)
例如现有 1、2、3、4、5、6 个 cell, 将 3 设置为 reorderable: false, 当我拖动 5 往上移动时,移动到 3 和 4 之间时,1234-6 均不会移动,移动到 2-3 之间时,列表会变成 12-346。这是因为 3 的可放置属性为 false,因此不会响应用户悬停在 3-4 之间的逻辑。
-
针对凑单条:
我们将高度设置为 0px 来隐藏,在用户放置时通过可视区列表来保障放置位置的准确性。
针对顶部筛选项、催领条:
我们在管理态保留 1px 的高度,既可以响应商品拖至顶部的操作,也能当用户正好放置在此处时,根据是否是 filter 组件来判断是否需要调整放置位置,如果需要调整,将该商品放置在第一个商品的位置。
针对底部的信息流(appear 锚点、错误页、卡片等):
通过 feeds 判断,将视图放置在信息流处的商品通通放置在最后一个商品后,避免出现商品放在信息流下面的异常情况。
针对信息流嵌套列表:
嵌套列表可能导致无限滚动,我们设置拖拽时触发的 list 滚动,属于外层容器滚动,而嵌套的信息流为内侧滚动容器,因此就算用户拖起后一直停留在屏幕底部,也不会触发最多一屏的信息流滚动,避免了拖拽时无限滚动的情况发生。
长按店铺头时,会主动收起该店铺的所有商品。视觉上的表现就是将组头变成缩略图,下方的商品卡隐藏。 放置整个店铺时,会将组头恢复,并展开所有的店铺商品。视觉上的表现就是,就是将缩略图恢复成组头,同时展示商品卡。
店铺头和悬浮元素的切换 商品卡的收起 Weex 的放置动画 下方元素与拖动区域的间距不突变
修复前效果 |
修复后效果 |
时刻 1:高度 400。在用户长按前。100 + 200 +100
时刻 2:高度 500。长按生成悬浮元素的一瞬间。200 + 200 + 100 (组头 100 上方显示了悬浮元素,导致整个列表往下抖动了)
时刻 3:高度 500。长按生成悬浮元素后,隐藏多余元素。与时刻 2 的高度一致 商品卡+红框被隐藏,但下方的店铺并未及时跟上
时刻 4:高度 100。长按逻辑结束。 整体变成了 100 的高度(恢复成了组头原本的高度,导致下方列表被盖住了)
-
在商品卡高度未改变时,列表中的拖起元素的高度会被设置为 proxyChild 的
高度
因为商卡高度未变、拖起元素占位高度变高,因此会导致 list 的下方元素先闪到下面,再因为商卡高度变小逐渐移动到上方。
解法:
需要后置拖起元素在 list 的占位高度变为 proxyChild 高度的时机,在
修复前录屏 |
修复后录屏 |
时刻 1:高度 200。 在用户松手前。悬浮元素高度 200, 列表占位 200。
时刻 2:高度 600。 松手的一瞬间,悬浮元素消失,商品卡展示。展示占位 200 的元素+两个商品卡。(200+200*2)
时刻 3:高度 500。恢复商品卡的展示。高度恢复为拖动前的高度。(100+200*2)
预期情况应该是在松手后,原本高度为 200 的整个悬浮区域一帧变成高度为 500 的整个店铺,且下方店铺紧随悬浮元素后。
具体原因:
在松手、且商品卡高度未改变时,列表中的占位的高度会被设置为 proxyChild 的高度
因为商卡高度未变、拖起元素占位高度变高,因此会导致 list 的下方元素先闪到下面,再因为占位恢复时回归本身的位置。
解法:
需要前置拖起元素在 list 的占位高度恢复为原生高度的时机,在商卡高度变高的同时执行。
松手时:统一松手后的 API 设计。之前存在 onReorderEnd 与 onReorder 两个时机。 前端会在 onReorderEnd 中做前端的恢复展示逻辑,在 onReorder 处理换位置后的插入逻辑。 这两个时机不同导致渲染时存在一个先后顺序,时机上无法统一。 将这两个时机统一收敛到 onReorder 中,前端把所有的逻辑放在 onReorder 中,并在 Insertbefore 后调用 endReorder 通知 Weex 调顺序完成,保证 Weex 复位动画和商品展示在同一时机执行,视觉效果不抖。

优网科技秉承"专业团队、品质服务" 的经营理念,诚信务实的服务了近万家客户,成为众多世界500强、集团和上市公司的长期合作伙伴!
优网科技成立于2001年,擅长网站建设、网站与各类业务系统深度整合,致力于提供完善的企业互联网解决方案。优网科技提供PC端网站建设(品牌展示型、官方门户型、营销商务型、电子商务型、信息门户型、微信小程序定制开发、移动端应用(手机站、APP开发)、微信定制开发(微信官网、微信商城、企业微信)等一系列互联网应用服务。