• 售前

  • 售后

热门帖子
入门百科

小步调原生实现左滑抽屉菜单

[复制链接]
丁侦球 显示全部楼层 发表于 2021-8-14 15:02:58 |阅读模式 打印 上一主题 下一主题
目次


  • WXS 相应变乱
  • 方案A

    • 页面布局和样式

  • WXS 变乱回调函数
  • WXS 脚本
  • 遮罩层
  • 方案B
  • 为什么要使用 WXS
  • 结语 & 参考资料
  • 参考资料:
在移动端,侧滑菜单是一个很常用的组件(通常称作 Drawer,抽屉)。由于现在手机屏幕太大,点击角落的菜单按钮明显不如在屏幕中心滑动方便。
相比其他平台,小程序的组件库支持明显还不够美满,各个框架也还不太成熟。由于之前使用框架的过程中被各种机密bug搞的头秃,照旧用回了原生情况。
最近研究了一下如安在原生框架中实现滑动抽屉菜单结果,本来以为很贫困,结果发现其实只必要几十行代码,而且可以类比实现很多机动的结果。感觉现在网上相关资料较少,因此在此分享一下。除了文中贴出的代码块,也可以点击链接在小程序开发工具中预览结果、检察代码片断。这里实现了三种常见结果,先看一下动图,下面将逐一解说代码实现。
A 菜单在上层

A2 菜单在上层,下层遮罩

B 菜单在下层



WXS 相应变乱


手势控制菜单的原理很简单:小程序提供了一系列触摸手势触发的变乱,包罗触摸开始、移动、竣事(
  1. touchstart, touchmove, touchend
复制代码
)等等。在这些变乱上绑定自定义的变乱相应函数,即可实现根据手势打开关闭菜单的操纵。
出于性能考虑,变乱处理函数最好放在 WXS、而不是 JS 文件中。详细原理与小程序的运行情况有关,感爱好的话可以去文末检察。WXS 是小程序的专用脚本语言(WXS 与 JS 的关系相当于 WXSS 与 CSS 的关系),语法和 JS 类似,有部分区别,比如:
       
  • 与 JS 隔离,不能调用其他 JavaScript 文件中定义的函数,也不能调用小程序提供的API   
  • 只能相应小程序内置组件的变乱,不支持自定义组件的变乱回调   
  • 变量与函数默以为模块私有,通过
    1. module.exports
    复制代码
    对外暴露   
  • 使用标签在 WXML 中引入使用(必须使用相对路径)
wxs 文件和 wxml 文件中的基本写法如下:
  1. // index.wxs
  2. function touchStart(e, ins) {}
  3. function touchMove(e, ins) {}
  4. function touchEnd(e, ins) {}
  5. module.exports = {
  6.   touchstart: touchStart,
  7.   touchmove: touchMove,
  8.   touchend: touchEnd
  9. }
  10. <wxs module="drawer" src="./index.wxs"></wxs>
  11. <view bindtouchstart="{{drawer.touchstart}}"
  12.       bindtouchmove="{{drawer.touchmove}}"
  13.       bindtouchend="{{drawer.touchend}}">
  14. </view>
复制代码
方案A



页面布局和样式



这是最常见的抽屉菜单样式之一,滑动主体内容不动,菜单在上层表现。起首写出基本的 HTML 布局和 CSS 样式(省略了一些雅观方面的样式表):
  1. <wxs module="drawer" src="./index.wxs"></wxs>
  2. <view>
  3.   <view class="main" bindtouchstart="{{drawer.touchstart}}"
  4.     bindtouchmove="{{drawer.touchmove}}" bindtouchend="{{drawer.touchend}}">
  5.     <view>
  6.       右滑显示侧边菜单 方案A
  7.     </view>
  8.   </view>
  9.   <view class="drawer" data-drawerwidth="150">
  10.     <view class="drawer-item">drawerA</view>
  11.     <view wx:for="{{[1, 2, 3]}}" class="drawer-item">
  12.       <text>menu item {{item}}</text>
  13.     </view>
  14.   </view>
  15. </view>
复制代码
WXML 中的几个重点:
       
  • 准确引入 wxs 模块(必须用相对路径)   
  • 举行滑动手势时菜单是埋伏的,所以现实上是在主界面上举行滑动,所以三个滑动变乱回调必要绑定在主体内容的 view 上面   
  • 举行移动的是 .drawer 元素,必要设置好 class 属性方便获取   
  • 抽屉元素的 data-drawerwidth 属性通过 dataset 传值给 wxs 脚本,规定了菜单的宽度,必要和样式保持同等
WXSS 没啥好说的,写在注释里了:
  1. .main {
  2.   height: 100vh;
  3.   width: 100%;
  4.   position: absolute;
  5. }
  6. .drawer {
  7.   height: 100vh;
  8.   width: 150px;
  9.   position: absolute;
  10.   transition: transform 0.4s ease; /* 位移使用transform实现,加个过渡动画更顺滑 */
  11.   left: -150px;  /* width、偏移与WXML中的数值保持一致,初始状态隐藏菜单 */
  12. }
复制代码
WXS 变乱回调函数


wxs 函数有两个入参
       
    1. event
    复制代码
    是小程序变乱对象,并在此基础上多了触发变乱的组件的实例
    1. event.instance
    复制代码
       
    1. ownerInstance
    复制代码
    是触发变乱的组件的父组件(页面)的实例
wxs 中组件实例是封装好的
  1. ComponentDescriptor
复制代码
对象,可以大概操纵组件的 dataset、设置 style、class 等,对于交互动画基本够用了。更多用法可参考文档。
  1. var wxsFunction = function(event, ownerInstance) {
  2.     var instance = ownerInstance.selectComponent('.classSelector') // 返回组件的实例
  3.     instance.setStyle({
  4.         "font-size": "14px" // 支持rpx
  5.     })
  6.     instance.getDataset()
  7.     instance.setClass(className)
  8.     return false // 不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault
  9. }
复制代码
WXS 脚本


条件判断为主,逻辑没啥特殊的,联合情景不难明白
       
  • 不要用 let, const 声明变量,会报错   
  • 把设置 transform 属性 X 位移的代码简单封装一下,看起来更雅观   
  • judge point 类似于吸附结果,就是菜单划出来超过某一位置就主动把剩余部分打开
  1. var startmark = 0;
  2. var status = 0;  // 菜单开闭状态
  3. var JUDGEPOINT = 0.7;
  4. function touchStart(e, ins) {
  5.   var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
  6.   startmark = pageX;
  7. }
  8. function touchMove(e, ins) {
  9.   var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
  10.   var offset = pageX - startmark;
  11.   var drawerComp = ins.selectComponent('.drawer');
  12.   var drawerWidth = drawerComp.getDataset().drawerwidth;
  13.   if (offset > 0 && status == 0) {
  14.     setCompTransX(drawerComp, Math.min(drawerWidth, offset))
  15.   } else if (offset < 0 && status == 1) {
  16.     setCompTransX(drawerComp, Math.max(0, offset))
  17.   }
  18. }
  19. function touchEnd(e, ins) {
  20.   var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
  21.   var offset = pageX - startmark;
  22.   var drawerComp = ins.selectComponent('.drawer');
  23.   var drawerWidth = drawerComp.getDataset().drawerwidth;
  24.   if (offset > 0 && status == 0) {
  25.     if (offset < drawerWidth * JUDGEPOINT) {
  26.       setCompTransX(drawerComp, 0);
  27.     } else {
  28.       setCompTransX(drawerComp, drawerWidth);
  29.       status = 1;
  30.     }
  31.   } else if (offset < 0) {
  32.     setCompTransX(drawerComp, 0);
  33.     status = 0;
  34.   }
  35. }
  36. function setCompTransX(comp, x) {
  37.   comp.setStyle({
  38.     transform: 'translateX(' + x + 'px)',
  39.   })
  40. }
  41. module.exports = {
  42.   touchstart: touchStart,
  43.   touchmove: touchMove,
  44.   touchend: touchEnd
  45. }
复制代码
遮罩层


点击文首或文末链接在小程序开发工具中检察完整代码。
遮罩层只必要在菜单和主容器之间增长一个 view 即可:
  1. <view class="main"></view>
  2. <view class="mask" data-maxopacity="0.6"></view>
  3. <view class="drawer" data-drawerwidth="150"></view>
复制代码
样式中很重要的是这个 pointer-events 属性,设置为 none 之后点击动作会穿透这个 view 到达下层。由于遮罩层不像抽屉是处在画面以外的,它虽然透明度为0,但现实上不停覆盖在 .main 上方,如果不加这个属性,全部对 .main 的点击操纵都会点到 .mask 上面,那不管是滑动照旧其他按钮都无效了。
  1. .mask {
  2.   height: 100vh;
  3.   width: 100%;
  4.   position: fixed;
  5.   transition: opacity 0.4s ease;
  6.   opacity: 0;
  7.   pointer-events: none;
  8.   background-color: #548CA8;
  9. }
复制代码
wxs 脚本也基本完全同等,只必要以相似的方法获取到 .mask 的实例以及 dataset 中的透明度参数,并在设置位移属性的同时设置遮罩层的透明度属性即可。
  1. function setDrawer(x) {
  2.   setCompTransX(drawerComp, x);
  3.   maskComp.setStyle({
  4.     opacity: x / drawerWidth * maskOpacity,
  5.   })
  6. }
复制代码
方案B


点击文首或文末链接在小程序开发工具中检察完整代码。
方案B 与方案A 的区别主要在于滑动时是主界面向右移动露出下层的菜单,别的各部分实现并无差异。这里只贴出主要差异的部分。
由于移动的是 .main 元素,因此把宽度设置数据放到了该元素的标签中,这样可以少获取一个组件实例。
  1. <view class="drawer"></view>
  2. <view class="main"
  3.       data-drawerwidth="150"
  4.       bindtouchstart="{{drawer.touchstart}}"
  5.       bindtouchmove="{{drawer.touchmove}}"
  6.       bindtouchend="{{drawer.touchend}}">
  7. </view>
复制代码
transition 动画属性也放在 .main 中,.drawer 的偏移不必要了。
  1. .main {
  2.   height: 100vh;
  3.   width: 100%;
  4.   position: absolute;
  5.   transition: transform 0.4s ease;
  6. }
  7. .drawer {
  8.   height: 100vh;
  9.   width: 150px;
  10.   position: absolute;
  11. }
复制代码
wxs 脚本中除了获取的组件差异外,连设置位移都不必要改。
  1. function touchMove(e, ins) {
  2.   var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
  3.   var offset = pageX - startmark;
  4.   var mainComp = ins.selectComponent('.main');
  5.   var drawerWidth = mainComp.getDataset().drawerwidth;
  6.   if (offset > 0 && status == 0) {
  7.     setCompTransX(mainComp, Math.min(drawerWidth, offset))
  8.   } else if (offset < 0 && status == 1) {
  9.     setCompTransX(mainComp, Math.max(0, offset))
  10.   }
  11. }
复制代码
为什么要使用 WXS


小程序在很多地方与 web 开发很像,但底层存在一些区别。网页中,渲染和脚本执行在同一个线程中执行(因此执行脚本大概会导致页面整个卡死);小程序在差异的线程中分别运行逻辑层(JS脚本)和渲染层(WXML和WXSS),线程间经过客户端(Native)举行通讯。

因此,如果使用 JS 脚本相应变乱,每次触发 touchmove 都会产生两次历程间通讯(下图左所示),通讯开销较大;同时“setData 渲染也会壅闭别的脚本执行”(文档这么说的,我也不知道为什么)。由于一次手势会触发巨量的 touchmove 变乱,上述原因会造成动画的卡顿。
而 WXS 函数运行在视图层,不存在上述问题(下图右所示)。


结语 & 参考资料


以上就是原生小程序的几种抽屉菜单实现方法,盼望对你有所资助;对于文中存在的疏漏欢迎讨论指正。
点击链接可以在小程序开发工具中检察完整代码(使用小程序开发工具的代码片断分享,对开发工具版本有肯定要求)。他这个分享代码片断有点玄学,如果直接打开失败,可以在登录后实验在“项目-导入代码片断”中直接输入链接或链接末了一段ID。

参考资料:

小程序框架/视图层/变乱体系/WXS 相应变乱
官方 demo
小程序宿主情况
到此这篇关于小程序原生实现左滑抽屉菜单的文章就先容到这了,更多相关小程序 左滑抽屉菜单内容请搜刮草根技术分享以前的文章或继承欣赏下面的相关文章盼望大家以后多多支持草根技术分享!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作