uni-app小程序开发:可拖动吸附式操作栏

307 阅读1分钟

效果展示

屏幕录制2023-08-05-09.31.23.gif

体验效果

yaoyao.png

实现步骤

HTML部分

<movable-area class="movableArea">
  <movable-view class="sidebar"
                :style="borderRadiusStyle"
                :x="x"
                :y="y"
                direction="all"
                :animation="false"
                @change="onChange"
                @touchstart.prevent="onTouchStart"
                @touchend.prevent="onTouchend">
    <view class="sidebar-more">
      <image :src="moreSvg"/>
    </view>
  </movable-view>
</movable-area>

js部分

data() {
  return {
    moreSvg,
    isTouch: false,
    direction: 'right',
    x: 0,
    y: 0,
    x1: 0,
    x2: 0,
    y1: 0,
    y2: 0,
    move: {
      x: 0,
      y: 0
    }
  }
},

拖动时设置为全圆,并根据操作栏位置设置圆角

computed: {
  borderRadiusStyle() {
    if (this.isTouch) {
      return 'border-radius: 100rpx;'
    }
    if (this.direction === 'right') {
      return 'border-radius: 100rpx 0 0 100rpx;'
    }
    if (this.direction === 'left') {
      return 'border-radius: 0 100rpx 100rpx 0;'
    }
    return ''
  }
},

确定操作栏初始位置,使用 Storage 存储坐标

mounted() {
  //确定初始位置
  uni.getSystemInfo({
    success: (res) => {
      const pxToRpx = 750 / res.windowWidth
      this.x1 = 0;
      this.x2 = Number(res.windowWidth) - 100 / pxToRpx;
      this.y1 = 0;
      this.y2 = Number(res.windowHeight) - 40 / pxToRpx;
      //获取缓存坐标
      const bar = uni.getStorageSync('sidebar')
      this.direction = bar && bar.x < this.x2 / 2 ? 'left' : 'right'
      this.$nextTick(() => {
        this.y = bar ? Number(bar.y) : Number(this.y2 * 0.6);
        this.x = bar ? Number(bar.x) : Number(this.x2);
        this.move.x = this.x;
        this.move.y = this.y;
      })
    }
  })
},

在组件销毁前储存当前位置

beforeDestroy() {
  try {
    if (this.x < this.x2 / 2) {
      this.x = this.x1;
    } else {
      this.x = this.x2;
    }
    uni.setStorageSync('sidebar', {x: this.x, y: this.y})
  } catch (e) {
  }
},

拖动时相关代码

onChange(e) {
  if (e.detail.source === "touch") {
    this.isTouch = true
    this.move.x = e.detail.x;
    this.move.y = e.detail.y;
  }
},
onTouchStart() {
  clearTimeout(this.loop)// 再次清空定时器,防止重复注册定时器(会把点击事件也阻止掉)
  this.isTouch = false
  this.loop = setTimeout(() => {
    this.isTouch = true
  }, 600)
},
onTouchend() {
  clearTimeout(this.loop) // 清空定时器,防止重复注册定时器
  if (!this.isTouch) {
    //没到触碰时间,触发点击事件
    this.onMoreClick()
    return
  }
  this.showList = false
  this.x = this.move.x;
  this.y = this.move.y;
  setTimeout(() => {
    if (this.move.x < this.x2 / 2) {
      this.x = this.x1;
      this.direction = 'left'
    } else {
      this.x = this.x2;
      this.direction = 'right'
    }
    this.isTouch = false
  }, 100)
}

css部分

.movableArea {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none; //设置area元素不可点击,则事件便会下移至页面下层元素
  z-index: 100;
}

.sidebar {
  pointer-events: auto; //可以点击
  height: 100rpx;
  width: max-content;
  background: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 2rpx 4rpx 12rpx 4rpx rgba(0, 0, 0, 0.04);

  &-more {
    margin: 0 10rpx;
    width: 80rpx;
    height: 80rpx;

    image {
      width: 80rpx;
      height: 80rpx;
    }
  }
}

最后(代码地址)

省略了中间的操作列表,感兴趣的可以去看完整代码,代码在 GitHub