「大众点评点餐」小程序开发经验 03:事件联动

文 | 李超

李超,美团点评前端开发工程师,2 年 web 开发经验,现在是美团点评点餐团队的一员。

在我们团队的小程序开发经验系列多篇文章发布以后,你是否对小程序视图层逻辑层,以及官方 API 文档有更为深入的学习和了解呢?

「纸上谈兵」很容易,「打好胜仗」才是关键。

今天,知晓程序(微信号 zxcx0101)为大家分享,开发「大众点评点餐小程序」菜单页面的过程中,遇到的问题和解决方案。

产品需求与最终效果

如果你看过我们的系列文章, 应该对我们的产品形态有了初步了解。

我们是做点餐菜单服务,菜单需要分类,需要购物车模块,那么典型的「工」字型布局是我们的首选。

大体结构为:顶部商家名称,可能会出现黄色横条提示模块;下方左侧为导航菜单栏;下方右侧为每个菜单分类包含的菜品展示列表;底部可能出现购物车模块。

结合上面的图片,菜单页的结构的交互需求很容易就整理出来了:

  • 顶部要求显示商家名称,有分享功能。
  • 下方左侧、右侧可分开滚动,滚动左侧不影响右侧,滚动右侧左侧随之联动高亮显示所在的菜单分类。
  • 点击下方左侧导航菜单栏,高亮显示被点击的菜单分类,下方右侧对应分类详情模块顶部与右侧滚动区的顶部重合(类似于 HTML 里的锚点功能)。
  • 滚动下方右侧菜品分类详情时,当该分类详情模块顶部接触到滚动区域的顶部,左侧对应的导航菜单栏高亮。
  • 若左侧高亮的导航菜单不在可视区域:
    • 当高亮的导航菜单顶部在左侧 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 之前,我们需要考虑其影响因素:

  • 设备高度
  • 黄条文案提示模块的存在
  • 购物车模块的存在
  • rpxpx 的转换

设备高度可以通过微信官方 API getSystemInfo 接口进行获取。

那么,该什么时候调用接口?

首先这是一个异步 API 接口,另外其直接受系统权限控制的影响,基于这两点因素,其结果返回的时机就不是确定的

我们可以在小程序启动时在 onLaunch 中调用该 API,然后将获取的结果放入到全局变量 globalData 中。

globalData 是挂在在全局 App 元素上的属性,对所有页面均可见。

现在来看看,利用系统信息接口获取到的数据是如何的:

这里的 windowHeightwindowWidth 指的是屏幕高度和宽度,且使用的单位是 px

在实际代码中,调用系统信息的接口代码就是这个样子:

计算 fixed 元素高度

黄条文案提示模块、购物车模块的高度都是已知的。

但大家应该记得这样的设计细节:所有的元素统一使用 rpx 做单位,而这里需要使用 px 作单位,必须要进行单位转换

rpxpx 的转换

大家对 375 这个数字是否有疑问呢?该比值是否会受到设备实际像素点的影响呢?实际上,你并不需要担心它。

同样的道理,我们可以得到购物车模块的高度 cartBarHeight

通过公式:windowScrollHeight = windowHeight - yellowBarHeight - cartBarHeight,可以计算得出两个 scroll-view 的滚动高度。

左侧栏与右侧栏的联动

首先我们要做到:点击左侧导航菜单栏,右侧定位到对应的分类菜品详情。

通过查看 scroll-view 的相关文档,我们发现,可以使用 scroll-into-view 属性,自动定位右侧需要滚动到的具体位置。

首先给左侧导航菜单栏绑定 tap 事件监听函数,事件触发后获取 event 对象象的 currentTarget 属性,取出渲染时存放在该节点上的分类 id,用此 id 作为唯一标识定位右侧分类详情。

然后,设置右侧 scroll-viewscroll-into-view 属性,这时,它会将右侧 scroll-viewid 属性值为该值的节点滚动到滚动区域的顶部。

点击事件监听函数

LEFT_TO_RIGHT_SUFFIX 是全局定义的常量,只是为了方便大家阅读,才将其写入函数内部,用作 id 拼接,保证唯一性。

在开发阶段,我们曾经尝试直接将获取到的 id 作为 rightToView 的值,也就是设定右侧 scroll-viewscroll-into-view 属性。

但我们发现,右侧 scroll-view 不会因此滚动到指定的高度

我们猜想,可能是因为获取到的 dataset.id 是一个数字类型字符串,其内部使用 === 方式导致不匹配。

另外需要注意的是,设置 scroll-into-view 引起的滚动操作,同样会触发 scroll 事件

右侧栏滚动事件与分类栏自动滚动

滑动右侧、让左侧滚动,是整个页面设计最核心的部分。

由于小程序无法获取元素的宽高,位置信息,滚动右侧实现左侧联动效果的实现难度非常高。

如何准确的获取右侧滚动到的具体分类,并让左侧导航菜单栏相应分类高亮,且在可视的范围内?

在设计阶段,我们和设计同学确认右侧每个视觉模块固定的高度,包括菜品模块高度、分类小灰条高度等。

这样,我们就可以根据已有的数据结构,计算出每个元素距离文档区顶部的高度。

左侧导航菜单栏高亮分类切换的边界条件为:右侧分类菜单详情的分类小灰条顶部,与右侧滚动区顶部重合。

我们需要做的,就是计算出每个分类小灰条距离文档顶部的高度 scrollHeight,并在每次滚动事件触发时,比较当前滚动的高度与分类小灰条的滚动高度 scrollHeight

这样做,就可确定当前在哪个分类菜单详情区域内,从而实现左侧分类导航栏的高亮。

长度单位误差

在测试时发现,有些机型滚动下方右侧 scroll-view 时,在边界条件出现时并不会完成左侧导航菜单栏高亮分类的切换,往往存在 10 px100 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-viewscroll-into-view,或者在 scroll-top 属性里完成滚动。

需要注意的是,若同时设置了 scroll-into-viewscroll-top 属性,小程序会优先使用 scroll-into-view 属性。

因此,如果这里使用 scroll-top 属性进行滚动,需要将 scroll-into-viwe 属性置空。

其他优化

联动功能开发完之后,我们的这款小程序遇到了性能瓶颈。

经过我们排查,我们发现:我们复用的 C 端的数据接口中,存在大量无用的对象属性,而这个数据结构直接作为页面渲染的 data 数据。

我们推荐的做法,就是简化 data 数据结构,只存放影响页面渲染的数据。这样做能够大幅度降低 UI 渲染时间,给用户更加流畅的体验。

总结与感受

微信小程序算是这两年非常火的一门新技术了。如何使用已经支持的功能特性来设计、开发产品是保障项目顺利完成的重要环节。

而在开发过程中,专注细节实现、吃透 API 文档,让用户感受到我们开发小程序的诚意是非常重要的,千万不能粗糙地做产品复制

在小程序发布那段时间,总能看到各种对小程序未来的设想,有悲观的,有观望的,也有激进的。

我个人认为,「赶鸭子上架」的思路并不可取,必须清楚自己的产品定位

你的产品是否满足「一次性消费」理念?内容是否不足以吸引用户下载你的 app?小程序是否比你的 HTML 5 更加具有吸引力?

这些都是需要我们进行思考的。

原文地址:

https://juejin.im/post/58b8cdefac502e006c04d626

原文发布于微信公众号 - 知晓程序(zxcx0101)

原文发表时间:2017-03-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IMWeb前端团队

移动端重构实战系列6——icon与图片

本文作者:IMWeb 结一 原文出处:IMWeb社区 未经同意,禁止转载 ”本系列教程为实战教程,是本人移动端重构经验及思想的一次总结,也是对sand...

1955
来自专栏程序员互动联盟

【专业领域】你所不知道的html5与html中的那些事(五)——web图像

文章简介: 现在的页面,一般都离不开图像,而怎么做才能让我们的页面中的图像加载的又快又好呢?在优化页面速度的时候还有什么事是你所不知道的呢? 下面看看...

2627
来自专栏腾讯社交用户体验设计

你离高效制作动画只差一篇文章的距离

1582
来自专栏前端知识分享

第132天:移动web端-rem布局(进阶)

      该方案使用相当简单,把下面这段已压缩过的 原生JS(仅1kb,源码已在文章底部更新,2017/5/3) 放到 HTML 的 head 标签中即可(注...

1893
来自专栏Google Dart

Flutte部件目录-布局

一个部件,将其子部件的体积缩小到可用空间的一部分。有关布局算法的更多详细信息,请参阅RenderFractionallySizedOverflowBox。

951
来自专栏Web 开发

2014年CSS报告

国外有人把Alex的TOP1000的CSS给爬了一遍,然后做了一些统计,蛮有意思的。

1070
来自专栏双十二技术哥

Android性能优化(二)之布局优化面面观

通过《Android性能优化(一)之启动加速35%》我们获得了闪电般的App启动速度,那么在应用启动完毕之后,UI布局也会对App的性能产生比较大的影响,如果布...

953
来自专栏Android-JessYan

骚年你的屏幕适配方式该升级了!-smallestWidth 限定符适配方案

原文地址: https://www.jianshu.com/p/2aded8bb6ede

862
来自专栏Guangdong Qi

iOS开发常用之网络

1261
来自专栏xingoo, 一个梦想做发明家的程序员

CSS布局那点事儿

布局 最开始老的一代网站开发,布局都是通过表格实现的。 这样可以形成规整的网格布局,但是也会带来一定的复杂性。比如想要新增某个页面元素,就有可能要改动整个表格...

2345

扫码关注云+社区

领取腾讯云代金券