公司移动端开发平台进行了大变革,前端架构由DCloud大生态转换为VUE,因此移动端的UI组件库从MUI改成使用MintUI,而后开始大刀阔斧的把MintUI组件改为MUI组件的样子,而后发现少了几个较为经常使用的,其中一个就是,嗯,侧滑面板(也叫侧滑菜单,也叫抽屉面板-andriod官方是这么翻译的,很形象)。可是,它就是一个布局组件,具体里边菜单什么的,那都是浮云(嗯,就是爱用几年前的流行词汇,并且很喜欢在网上冲浪和踩别人的空间)css
(1)主面板滑动,侧滑面板不动
(2)侧滑面板动,主面板不动
(3)它俩一块动,一块儿--------前端
mintUI组件中一样?️滑动操做的tabContainer,为了知足需求5,我连函数名都抄了过来。vue
不说废话,上代码吧git
这个组件分为两部分,一部分为侧滑面板容器,另外一部分为主面板容器,而后具体容器内部直接放了插槽,而后还须要一个主面板容器的遮罩,为了侧滑面板打开的时候显现出来。上代码了github
<div class="mint-drawer-layout"> <!--侧滑栏--> <div ref="drawer" class=" mint-drawer-warp" @touchstart.stop="startDrag" @touchmove.stop="onDrag" @touchend.stop="endDrag" :style="drawerStyle"> <slot name="drawer"></slot> </div> <!--主容器--> <div ref="content" @touchstart.stop="startDrag" @touchmove.stop="onDrag" @touchend.stop="endDrag" class=" mint-content-warp" :style="contentStyle"> <!--主容器遮罩(侧滑打开状态下显示)--> <div class="content-mask" v-tap="toggle" ref="contentMask"></div> <slot name="content"></slot> </div> </div>
这块加了一些咱们公司的一些需求,可能各位哥哥姐姐门用不到里边的一些props的设计,仅供参考web
props: { // 侧滑面板的宽度(单位px) 'drawerWidth': { type: Number, default: 200 }, // 是否可用 'enable': { type: Boolean, default: true }, // 侧滑菜单是否在右边,默认为false,在左边 'isRight': { type: Boolean, default: false }, // 侧滑菜单滑动操做类型 // ['fixDrawer'——固定侧滑面板,主面板滑动] // ['fixContent'——固定主面板,侧滑面板滑动] // ['noFixed'——一块儿滑动!] 'swipeType': { type: String, default: 'fixDrawer' }, // 点击出现侧滑菜单的按钮的id ( @TODO 这里如何处理异步渲染的问题 ) 'btnId': { type: String, default: '' }, // 状态位,侧滑面板是否为打开状态 // (由于咱们公司有这种一开始就把侧滑菜单打开的shabee场景,因此这才会有这么个东西) //(若是这个不但愿配置的话、能够放在data里边) 'isDrawerOpened': { type: Boolean, default: false }, // 是否可滑动,若是不可滑动的话,就只能经过调用toogle方法打开侧滑面板 // 这个也是公司的一个使用场景,就是你甭滑,找个按钮触发一下侧滑面板打开的方法才能打开 //(若是这个不但愿配置的话、也能够放在data里边) 'swipeable': { type: Boolean, default: true } }
不得不认可,个人css写的shitsegmentfault
对于整个的组件来讲,它应该是默认充满整个父容器的,并且这个组件,我以为,通常都是用来放在最外层的一个布局组件,因此,默认充满窗口就好了
因此组件的最外层来一个绝对布局,而后以下:架构
.mint-drawer-layout { position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow-x: hidden; }
而后侧滑面板,只须要纵向充满就能够了,宽度是配置app
.mint-drawer-warp { position: absolute; top: 0; bottom: 0; }
而后是主面板,没说的异步
.mint-content-warp { position: relative; width: 100%; height: 100%; }
终于来到了介绍到底该怎么实现的了
首先第一步上来就要设置一个记录滑动操做的状态变量——dragging,设置为false,方便屏蔽滑动时触发的其余操做的执行
——开始的时候记录开始滑动的位置
——滑动中,dragging状态记录为true,开始进行滑动位置和手指移动的联动
——滑动结束,dragging状态记录为false,计算当前的滑动位置,判断是划开侧滑面板仍是关闭,并进行动画处理
其中几个细节小谈一哈
(1)左右滑动操做触发的判断:我这边是公司的规范,横轴移动位移大于五,竖轴位移不大于横轴的1.73倍就能够
(2)最后结束时判断侧滑面板的打开和关闭:是这样的,我这边取的是三分之一的侧滑面板的宽度,也就是从打开到关闭,那么像关闭的方向滑动侧滑面板宽度的三分之一就能够了,若是是关闭到打开,往打开的方向滑动三分之一就能够了
(3)左侧和右侧,还有三种不一样的滑动方式:三种不一样的滑动方式实际上就是控制到底哪一个面板随着手指动,具体的动做过程和面板的偏移量其实是同样的。左右两侧就更简单了,直接是对称的操做就可
滑动结束的操做,参考的tabcontainer,也挺巧妙的,各位请上眼~
/** * 滑动结束的动画 */ swipeLeaveTransition() { let g = this, currentMovingDoms = []; let {swipeType, drawerWidth} = g; switch (swipeType) { case 'fixDrawer': currentMovingDoms.push(g.content); break; case 'fixContent': currentMovingDoms.push(g.drawer); break; case 'noFixed': currentMovingDoms.push(g.drawer); currentMovingDoms.push(g.content); break; default: break; } currentMovingDoms.forEach((val) => { val.classList.add('swipe-transition'); }); setTimeout(() => { if (g.isDO) { this.swipeMove(drawerWidth); } else { this.swipeMove(0); g.contentMask.style.opacity = 0; g.contentMask.style.display = 'none'; } g.isToggle = false; currentMovingDoms.forEach((val) => { once(val, 'webkitTransitionEnd', _ => { val.classList.remove('swipe-transition'); g.swiping = false; }); }); }, 0); }, /** * 滑动操做 * @param offset 滑动位置 */ swipeMove(offset) { let g = this; let {swipeType, isRight} = g; g.contentMask.style.display = 'block'; g.contentMask.style.opacity = Math.abs(offset) / g.drawerWidth * 0.4; switch (swipeType) { case 'fixDrawer': g.content.style.webkitTransform = `translate3d(${(!isRight ? '' : '-') + offset}px, 0, 0)`; g.swiping = true; break; case 'fixContent': g.drawer.style.webkitTransform = `translate3d(${(!isRight ? '' : '-') + offset}px, 0, 0)`; g.swiping = true; break; case 'noFixed': g.content.style.webkitTransform = `translate3d(${(!isRight ? '' : '-') + offset}px, 0, 0)`; g.drawer.style.webkitTransform = `translate3d(${(!isRight ? '' : '-') + offset}px, 0, 0)`; g.swiping = true; break; default: break; } }, // 开始滑动 startDrag(evt) { let g = this; if (!g.enable || !g.swipeable) return false; evt = evt.changedTouches ? evt.changedTouches[0] : evt; g.start.x = evt.pageX; g.start.y = evt.pageY; }, // 滑动中 onDrag(evt) { let g = this, swiping; if (!g.enable || !g.swipeable) return false; g.dragging = true; const e = evt.changedTouches ? evt.changedTouches[0] : evt; const offsetTop = e.pageY - g.start.y; const offsetLeft = e.pageX - g.start.x; const y = Math.abs(offsetTop); const x = Math.abs(offsetLeft); swiping = !(x < 5 || (x >= 5 && y >= x * 1.73)); if (!swiping) return; evt.preventDefault(); let offset; if (g.isDO) { offset = g.isRight ? (g.drawerWidth - offsetLeft) : (g.drawerWidth - (-offsetLeft)); } else { offset = g.isRight ? -offsetLeft : offsetLeft; } if (offset < 0 || offset > g.drawerWidth) { g.swiping = false; return; } g.offset = offset; g.swipeMove(offset); }, // 结束滑动 endDrag() { let g = this; if (!g.enable || g.isToggle || !g.dragging) { return false; } const tempWidth = g.drawerWidth / 3; if (g.isDO && g.offset < tempWidth * 2) { g.isDO = false; } else if (!g.isDO && g.offset > tempWidth) { g.isDO = true; } g.dragging = false; g.swipeLeaveTransition(); }
好啦,到这应该就差很少了。。。
里边涉及到的v-tap指令是自定义的指令,为了处理移动端的点击操做,我还整理了一片陋文:https://segmentfault.com/a/11... (移动点击长按滑动vue指令)
而后这个组件的源码我放在了我fork出来的mintUI项目上
https://github.com/LylaYuKako...
谢谢各位品尝,