文 | 李超
李超,美团点评前端开发工程师,2 年 web 开发经验,现在是美团点评点餐团队的一员。
在我们团队的小程序开发经验系列多篇文章发布以后,你是否对小程序视图层、逻辑层,以及官方 API 文档有更为深入的学习和了解呢?
「纸上谈兵」很容易,「打好胜仗」才是关键。
今天,知晓程序(微信号 zxcx0101)为大家分享,开发「大众点评点餐小程序」菜单页面的过程中,遇到的问题和解决方案。
产品需求与最终效果
如果你看过我们的系列文章, 应该对我们的产品形态有了初步了解。
我们是做点餐菜单服务,菜单需要分类,需要购物车模块,那么典型的「工」字型布局是我们的首选。
大体结构为:顶部商家名称,可能会出现黄色横条提示模块;下方左侧为导航菜单栏;下方右侧为每个菜单分类包含的菜品展示列表;底部可能出现购物车模块。
结合上面的图片,菜单页的结构的交互需求很容易就整理出来了:
scroll-view
滚动区上方(被遮住了),则将该高亮导航菜单滚动至将高亮导航栏的顶部与左侧可滚动区域顶部重合(高亮菜单为滚动区的第一个分类)。scroll-view
滚动区可视区下方,将高亮导航菜单滚动到屏幕中央区域。关键技术罗列
这里需要指出:产品在设计成稿之前,我们已经对小程序支持的功能做了细致的调研,确保可以通过技术手段实现产品需求,才确定 UI 以及交互设计。
从产品兼容性角度出发,我们考虑使用微信小程序的 rpx
作为 UI 设计的标准尺寸。
该尺寸和 rem
非常类似,不同点在于其对基准尺寸的设定。
rem
使用文档根元素设定的尺寸作为基准尺寸,而 rpx
使用手机屏幕宽度为基准,决定 1 rpx
对应的宽度,该动态尺寸对设备的兼容性更加友好。
此外,微信还自带 scroll-view
UI组件,并提供一系列组件状态操作接口。
当 scroll-view
组件滚动时,会触发 scroll
事件。所返回的 event
对象各项长度属性,均使用 px
作单位。
开始开发
菜单页面的结构如下:
我们在开发中使用到对文件实时编译的工具:
为方便代码维护以及日常的开发习惯,我们对 Less 语法进行兼容,引入了Promise
组件。
WXML 页面布局
这里着重考虑两个 scroll-view
结构设计,左右的布局结构可以使用 CSS 样式属性 float
,或者是 CSS 3 的 flex
。
另外,黄条提示和购物车模块,都可以用 fixed
属性搞定。
微信官方文档介绍,使用 scroll-view
组件,必须指定高度。
我们实践时发现,使用 scroll-view
可以不指定高度,页面有滚动区存在。但这么做,滚动时无法触发 scroll
事件,也就无法完成联动设计。
滚动区域检测
在这里,我们需要注意两点:
px
作为单位。scroll-view
上显式的指定其 height
属性在获取滚动区高度 windowScrollHeight
之前,我们需要考虑其影响因素:
rpx
到 px
的转换设备高度可以通过微信官方 API getSystemInfo
接口进行获取。
那么,该什么时候调用接口?
首先这是一个异步 API 接口,另外其直接受系统权限控制的影响,基于这两点因素,其结果返回的时机就不是确定的。
我们可以在小程序启动时在 onLaunch
中调用该 API,然后将获取的结果放入到全局变量 globalData
中。
而 globalData
是挂在在全局 App
元素上的属性,对所有页面均可见。
现在来看看,利用系统信息接口获取到的数据是如何的:
这里的 windowHeight
和 windowWidth
指的是屏幕高度和宽度,且使用的单位是 px
。
在实际代码中,调用系统信息的接口代码就是这个样子:
计算 fixed
元素高度
黄条文案提示模块、购物车模块的高度都是已知的。
但大家应该记得这样的设计细节:所有的元素统一使用 rpx
做单位,而这里需要使用 px
作单位,必须要进行单位转换。
从 rpx
到 px
的转换
大家对 375
这个数字是否有疑问呢?该比值是否会受到设备实际像素点的影响呢?实际上,你并不需要担心它。
同样的道理,我们可以得到购物车模块的高度 cartBarHeight
。
通过公式:windowScrollHeight = windowHeight - yellowBarHeight - cartBarHeight
,可以计算得出两个 scroll-view
的滚动高度。
左侧栏与右侧栏的联动
首先我们要做到:点击左侧导航菜单栏,右侧定位到对应的分类菜品详情。
通过查看 scroll-view
的相关文档,我们发现,可以使用 scroll-into-view
属性,自动定位右侧需要滚动到的具体位置。
首先给左侧导航菜单栏绑定 tap
事件监听函数,事件触发后获取 event
对象象的 currentTarget
属性,取出渲染时存放在该节点上的分类 id
,用此 id
作为唯一标识定位右侧分类详情。
然后,设置右侧 scroll-view
的 scroll-into-view
属性,这时,它会将右侧 scroll-view
上 id
属性值为该值的节点滚动到滚动区域的顶部。
点击事件监听函数
LEFT_TO_RIGHT_SUFFIX
是全局定义的常量,只是为了方便大家阅读,才将其写入函数内部,用作 id
拼接,保证唯一性。
在开发阶段,我们曾经尝试直接将获取到的 id
作为 rightToView
的值,也就是设定右侧 scroll-view
的 scroll-into-view
属性。
但我们发现,右侧 scroll-view
不会因此滚动到指定的高度。
我们猜想,可能是因为获取到的 dataset.id
是一个数字类型字符串,其内部使用 ===
方式导致不匹配。
另外需要注意的是,设置 scroll-into-view
引起的滚动操作,同样会触发 scroll
事件。
右侧栏滚动事件与分类栏自动滚动
滑动右侧、让左侧滚动,是整个页面设计最核心的部分。
由于小程序无法获取元素的宽高,位置信息,滚动右侧实现左侧联动效果的实现难度非常高。
如何准确的获取右侧滚动到的具体分类,并让左侧导航菜单栏相应分类高亮,且在可视的范围内?
在设计阶段,我们和设计同学确认右侧每个视觉模块固定的高度,包括菜品模块高度、分类小灰条高度等。
这样,我们就可以根据已有的数据结构,计算出每个元素距离文档区顶部的高度。
左侧导航菜单栏高亮分类切换的边界条件为:右侧分类菜单详情的分类小灰条顶部,与右侧滚动区顶部重合。
我们需要做的,就是计算出每个分类小灰条距离文档顶部的高度 scrollHeight
,并在每次滚动事件触发时,比较当前滚动的高度与分类小灰条的滚动高度 scrollHeight
。
这样做,就可确定当前在哪个分类菜单详情区域内,从而实现左侧分类导航栏的高亮。
长度单位误差
在测试时发现,有些机型滚动下方右侧 scroll-view
时,在边界条件出现时并不会完成左侧导航菜单栏高亮分类的切换,往往存在 10 px
到 100 px
的误差。
从产品角度,这种误差是不能容忍的。个人并不确定是什么原因导致误差的出现,但看起来并没有非常好的解决办法。
那么能用什么方案减少误差呢? 我的实现思路是「人工干预自动校正」。
仔细分析滚动事件返回的 event
对象:
在这里,我们需要特别留意 detail
中的 scrollHeight
。
滚动事件会给出整个 scroll-view
文档内容的高度,这个高度值非常关键,我们可以这样计算出来:
由于单个菜品详情高度与单个分类小灰条高度的高度比是确定的,所以上面的方程式为一元方程。
我们可以用它计算出单个菜品详情高度,以及单个分类小灰条高度,更新每个分类小灰条距离文档顶部的距离 scrollTop
值。
经测试发现,左侧导航菜单栏高亮分类的切换精度非常高,而且兼容性很好。
左侧高亮分类跳错
在实际开发中, 我还发现一个问题: 左侧有分类 A、B、C,点击分类 B,分类 B 高亮,右侧定位到分类 B 的详情区域,随之左侧高亮分类切换到 A 上。
想一想,这是什么原因导致的?
在上面讲解 scroll-view
属性时,我提到过一句话:
设置 scroll-into-view
引起的滚动操作,同样会触发 scroll
事件。
点击左侧分类,右侧由于 scroll-into-view
触发了滚动事件,而相应的滚动事件监听函数函数,计算得出当前高亮的导航菜单栏为 A,更新页面的 data
将高亮分类切换到了 A 上。
解决方案有这两种:
scroll
事件函数的执行。在这里,我推荐使用第二种方式。因为在不同机器上,硬件会存在细微差别,我们无法准确的设置误差范围。
具体的思路是这样的:若点击左侧导航菜单栏,设定全局锁定状态,若锁定则不右→左的联动操作,再解除锁定状态。
分类导航栏的可视性
通过上面「右→左」联动,我们已经可以让左侧随着右侧滚动而高亮。
但随之而来的问题是: 左侧也是一个 scroll-view
,如何保证高亮的分类,刚好在可视区域里(屏幕上)呢?
我们需要监听右侧滚动事件,判断当前在哪一个分类上,确定该分类在左侧 scroll-view
的文档高度,判断是否需要滚动左侧 scroll-view
。
我们可以通过 scroll-view
的 scroll-into-view
,或者在 scroll-top
属性里完成滚动。
需要注意的是,若同时设置了 scroll-into-view
和 scroll-top
属性,小程序会优先使用 scroll-into-view
属性。
因此,如果这里使用 scroll-top
属性进行滚动,需要将 scroll-into-viwe
属性置空。
其他优化
联动功能开发完之后,我们的这款小程序遇到了性能瓶颈。
经过我们排查,我们发现:我们复用的 C 端的数据接口中,存在大量无用的对象属性,而这个数据结构直接作为页面渲染的 data
数据。
我们推荐的做法,就是简化 data
数据结构,只存放影响页面渲染的数据。这样做能够大幅度降低 UI 渲染时间,给用户更加流畅的体验。
总结与感受
微信小程序算是这两年非常火的一门新技术了。如何使用已经支持的功能特性来设计、开发产品是保障项目顺利完成的重要环节。
而在开发过程中,专注细节实现、吃透 API 文档,让用户感受到我们开发小程序的诚意是非常重要的,千万不能粗糙地做产品复制。
在小程序发布那段时间,总能看到各种对小程序未来的设想,有悲观的,有观望的,也有激进的。
我个人认为,「赶鸭子上架」的思路并不可取,必须清楚自己的产品定位。
你的产品是否满足「一次性消费」理念?内容是否不足以吸引用户下载你的 app?小程序是否比你的 HTML 5 更加具有吸引力?
这些都是需要我们进行思考的。
原文地址:
https://juejin.im/post/58b8cdefac502e006c04d626