前言
为了方便用户跳转页面,小程序项目中经常会设置一些浮窗按钮。但是这些按钮有时会覆盖页面中的主操作,影响用户体验。
简介
基于以上背景,我们想让浮窗可以拖动。拖动的范围是手机主屏幕(顶部导航栏以下)的任意位置,但不要超出手机边界。
实现
方案一
小程序有movable-area
和movable-view
组件。
movable-area
: 为可移动区域,必须设置width
和height
属性,不设置默认为10px;movable-view
: 为可移动的视图容器,必须在movable-area
组件中,并且必须是直接子节点,否则不能移动;
由于我要实现的浮窗拖动功能所在页面都已开发完善,仅增加拖动功能,若使用movable-area
和movable-view
组件方式要对代码进行比较大的改动,因而放弃此方案。
方案二
小程序提供了touchmove
事件,该事件的事件对象提供了touches
属性。
小程序事件
因此我们可以设置浮窗样式为绝对定位,根据触摸移动位置设置其left和top值,并绑定
catchtouchmove
事件(touchmove
为冒泡事件,我们不想拖动浮窗时影响外层组件滚动等副作用,因此我们使用catch阻止冒泡)。
方法如下:
wxml部分
<view
style="left:{{left}}rpx; top:{{top}}rpx;"
catchtouchmove="setTouchMove"
@tap="backHome"
>
<text class="text">回到首页</text>
</view>
js部分
setTouchMove(e) {
this.left = e.touches[0].clientX;
this.top = e.touches[0].clientY;
}
** 当前方法会有以下问题:**
- 拖动严重卡顿,体验效果差;
- 会越出边界;
** 优化 **
- 针对问题一:可以增加时间间隔判断,避免过于频繁触发,自己通过测试找到最合适的时间间隔点,不至于卡顿,也不要过久导致延迟太长;另外,把left和top存在一个字段
btnXY
里,减少data的赋值。 - 针对问题二:我们要对上下左右四个边界做进一步的判断。即:当clientX和clientY小于0时,设置left或top为0,避免越出左边界和上边界。当clientX和clientY大于屏幕宽度和高度时,设置left和top为屏幕宽/高-浮窗宽/高。
具体实现:
wxml部分
<view
style="left:{{btnXY[0]}}rpx; top:{{btnXY[1]}}rpx;"
catchtouchmove="setTouchMove"
@tap="backHome"
>
<text class="text">回到首页</text>
</view>
setTouchMove(e) {
// 避免频繁setData
if (+new Date() - this.lastTime < 80) {
return;
};
// 鼠标位置
let clientX = e.touches[0].clientX;
let clientY = e.touches[0].clientY;
const X = this.btnSize;
const Y = this.btnSize + (this.isIpx ? 34 : 0);
// 控制边界
if (clientX < 0) {
clientX = 0;
} else if (clientX > this.windowXY[0] - X) {
clientX = 0;
}
if (clientY < 0) {
clientY = 0;
} else if (clientY > this.windowXY[1] - Y) {
clientY = this.isIpx ? 34 : 0;
}
this.btnXY = [clientX * 2, clientY * 2];
this.lastTime = +new Date();
}
屏幕宽高:
wepy.getSystemInfo().then(resp => {
if (resp) {
this.isIpx = resp.model.indexOf('iPhone X') > -1;
this.windowXY = [resp.windowWidth, resp.windowHeight];
this.$apply();
}
});
拓展
为了实现代码更好的复用,将用于拖动功能的data、事件等放在mixin中,后续再有类似功能可直接使用该mixin。