Vue中怎么使用DrawerLayout侧滑菜单组件
Vue中怎么使用DrawerLayout侧滑菜单组件,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
HTML结构
页面结构很简单,一个抽屉,一个主容器,内容可以利用slot支持外部自行定制。
<div class="drawer-layout">
<!--抽屉-->
<div class="drawer-wrap">
<slot name="drawer"></slot>
</div>
<!--主容器-->
<div class="content-wrap">
<!--遮罩-->
<div class="drawer-mask"></div>
<slot name="content"></slot>
</div>
</div>
抽屉一开始是隐藏在左侧屏幕外的,故设置 left:-100% 使其整个都藏在外部
使用Touch
首先,判断浏览器是否支持 touchEvent
let isTouch = 'ontouchstart' in window;
let mouseEvents = isTouch ?
{
down: 'touchstart',
move: 'touchmove',
up: 'touchend',
over: 'touchstart',
out: 'touchend'
} :
{
down: 'mousedown',
move: 'mousemove',
up: 'mouseup',
over: 'mouseover',
out: 'mouseout'
};
绑定 touchdown 事件
document.addEventListener(mouseEvents.down, initDrag, false);
先定义一些变量,手指按下的x坐标记为 startX ,滑动中手指的位置x坐标记为 nowX ,drawer的x坐标偏移量记为 startPos
let startX, nowX, startPos;
触发 touchstart 时,记录起始位置并绑定 touchmove ,注意:如果是 mouseEvent ,通过 e.clientX 来获取当前的x坐标,如果是 touchEvent ,要通过 e.changedTouches[0].clientX 来获取x坐标
const initDrag = function (e) {
startX = e.clientX || e.changedTouches[0].clientX; //记录手指按下的位置
startPos = this.pos; //记录drawer的上次位置
document.addEventListener(mouseEvents.move, drag, false);
document.addEventListener(mouseEvents.up, removeDrag, false);
}.bind(this);
const drag = function (e) {
nowX = e.clientX || e.changedTouches[0].clientX; //滑动中手指的位置x坐标
let pos = startPos + nowX - startX;
pos = Math.min(width, pos); //不能超过滑动最大值
pos = Math.max(0, pos); //不能小于0
this.pos = pos; //设置滚动距离为拖动的距离
}.bind(this);
那么,手指滑动的距离就是 nowX - startX ,当前drawer的位置为 startPos + nowX - startX ,这样抽屉已经跟随手指向右移动了,并且不会超过我们设置的拖动最大值。
区分垂直滑动和水平滑动
接下来你会发现一个问题,当手指垂直滚动主内容时,向右滑动手指也会拖出抽屉,这时应该做一件事:区分垂直滑动和水平滑动
当然,办法有很多,这里先介绍一种利用三角函数来判定的方法
假设,上图中的每个箭头是手指滑动的方向,绿色箭头代表可以拖出抽屉,红色箭头代表不可以拖出(注意,红色箭头也是有x坐标的偏移量的)。即当不可以拖出抽屉时,应触发默认事件,比如垂直方向的滚动等等。
当手指按下触发 touchstart 时,记录初始位置P 0 ;当滑动手指时,触发的第一次 touchmove 时,记录位置P 1 ,我们将P 0 到P 1 的矢量记为S(原谅我这个灵魂画手)
这时候很容易看出,∠θ大于某个值时,比如30度,就可能是垂直方向的滚动操作而不是拖动抽屉。所以,可以根据 y/x>tan30°
得到判断条件:
if (isVerticle === undefined) isVerticle = Math.abs(nowY - startY) / Math.abs(nowX - startX) > (Math.sqrt(3) / 3);
当 isVerticle 为 true 时,不执行drawer的拖动
让Drawer动起来
我们使用css3的 transition 属性使drawer具有过渡动画效果,这里写一个 moving 类
.moving
transition transform .3s ease
别忘了加上class绑定,拖动时是不需要过渡动画的(要跟随手指),而松开手指时才需要过渡动画。
<div class="drawer-wrap" :class="{'moving':moving,'will-change':willChange}"
:>
<slot name="drawer"></slot>
</div>
所以绑定 touchend 事件的方法时要做这些步骤
const removeDrag = function (e) {
if (isVerticle !== undefined) {
if (!isVerticle) {//当判定为抽屉拖动才进入
let pos = this.pos;
this.visible = pos > width * 3 / 5 //当前位置如果大于总宽度的3/5就判定为全部展开抽屉,否则将抽屉弹回隐藏
if (this.pos > 0 && this.pos < width) this.moving = true;//如果位置已经处于最小值或最大值处,不需要有动画效果了
}
this.pos = this.visible ? width : 0;
}
if (!this.moving) {
this.willChange = false; //留个悬念
}
isVerticle = undefined;
//取消touchmove和touchend事件绑定
document.removeEventListener(mouseEvents.move, drag, false);
document.removeEventListener(mouseEvents.up, removeDrag, false);
}.bind(this);
上面你可能发现代码里有个 this.willChange = false ,它是干啥的捏?下面我们请出css的 will-change 大法
.will-change
will-change transform
CSS 属性 will-change 为web开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。
其实是我们在 touchstart 可以预先告知浏览器抽屉可能要发生位移
const initDrag = function (e) {
//...
this.willChange = true;
}.bind(this);
当然最后别忘了在 transitionend 事件后把 transition 和 will-change 去掉,让浏览器歇一会儿~
还有什么可以优化的?
上面说的已经基本上把主要功能实现了,但是这其中还有没有哪里可以优化的?
咦? passive
是什么鬼?
网站使用被动事件侦听器以提升滚动性能,在您的触摸和滚轮事件侦听器上设置 passive 选项可提升滚动性能具体看这里
原来这是现代浏览器的一个新特性,我们需要以新的方式来绑定我们的touch事件,当然首先先检测一下是否支持 passive
const supportsPassive = (() => {
let supportsPassive = false;
try {
const opts = Object.defineProperty({}, 'passive', {
get: function () {
supportsPassive = true;
}
});
window.addEventListener("test", null, opts);
} catch (e) {
}
return supportsPassive;
})();
于是我们的绑定事件代码变成这样
document.addEventListener(mouseEvents.move, drag, supportsPassive ? {passive: true} : false);
看完上述内容,你们掌握Vue中怎么使用DrawerLayout侧滑菜单组件的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网行业资讯频道,感谢各位的阅读!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341