微信小程序浮窗拖动功能

2,284 阅读2分钟

前言

为了方便用户跳转页面,小程序项目中经常会设置一些浮窗按钮。但是这些按钮有时会覆盖页面中的主操作,影响用户体验。

简介

基于以上背景,我们想让浮窗可以拖动。拖动的范围是手机主屏幕(顶部导航栏以下)的任意位置,但不要超出手机边界。

实现

方案一

小程序有movable-areamovable-view组件。

  • movable-area: 为可移动区域,必须设置widthheight属性,不设置默认为10px;
  • movable-view: 为可移动的视图容器,必须在movable-area组件中,并且必须是直接子节点,否则不能移动;

由于我要实现的浮窗拖动功能所在页面都已开发完善,仅增加拖动功能,若使用movable-areamovable-view组件方式要对代码进行比较大的改动,因而放弃此方案。

方案二

小程序提供了touchmove事件,该事件的事件对象提供了touches属性。 小程序事件 touch对象 因此我们可以设置浮窗样式为绝对定位,根据触摸移动位置设置其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。