vue uniapp 自定义滑动数量选择

36 阅读1分钟
<template>
  <view class="container">
    <view class="dialog-box">
      <view class="active-box">
        <view class="num">
          <text>{{ num }}</text>
        </view>
      </view>
      <div
        class="scroll-view"
        @touchstart="touchtStarEvent"
        @touchmove="touchMoveEvent"
        @touchend="touchEndEvent"
      >
        <view class="box" :style="'left:' + scrollLeft + 'px'">
          <view
            v-for="(item, index) in selectorNum"
            :key="index"
            class="seize-seat"
            :style="{ width: boxRpx + 'rpx' }"
          >
            <text
              :style="{ width: boxRpx + 'rpx', left: -boxRpx / 2 + 'rpx' }"
              >{{ item + 1 }}</text
            >
          </view>
        </view>
        <view class="active"></view>
      </div>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      selectorNum: 30,
      num: 6,
      scrollLeft: 0,
      touchtStart: 0,
      boxRpx: 50, //50为每个盒子宽度,rpx
      boxWidth: 0, //每个盒子的宽度,px
      throttleStatus: false,
    };
  },
  created() {
    const screenWidth = 750 / uni.getSystemInfoSync().screenWidth; //计算字体比例
    const boxWidth = (this.boxWidth = parseInt(this.boxRpx / screenWidth)); //计算每个盒子的宽度
    this.scrollLeft = -(this.num - 1) * boxWidth;
  },
  methods: {
    touchtStarEvent(e) {
      this.touchtStart = e.changedTouches[0].clientX;
    },
    //每次节流设置left,都重置开始值,更准确的计算每次滑动
    // 按下滑动20px左右,才会触发滑动事件
    touchMoveEvent(e) {
      const clientX = e.changedTouches[0].clientX;
      const w = clientX - this.touchtStart;
      this.throttle(() => {
        this.scrollLeft += w;
        this.touchtStart = clientX;
      });
    },
    touchEndEvent() {
      const { scrollLeft, boxWidth, selectorNum } = this;
      const diff = Math.abs(scrollLeft) % boxWidth;
      // 滑动大于大于一半
      if (diff > boxWidth / 2) {
        this.scrollLeft = scrollLeft - (boxWidth - diff);
      } else {
        this.scrollLeft = scrollLeft + diff;
      }
      // 超过最小数量
      if (this.scrollLeft > 0) {
        this.scrollLeft = 0;
      }
      // 超过最大数量
      if (this.scrollLeft < -(selectorNum - 1) * boxWidth) {
        this.scrollLeft = -(selectorNum - 1) * boxWidth;
      }
      this.num = Math.ceil(Math.abs(this.scrollLeft / boxWidth) + 1);
    },
    // 节流
    throttle(fn, t = 10) {
      if (this.throttleStatus) return;
      this.throttleStatus = true;
      fn();
      setTimeout(() => {
        this.throttleStatus = false;
      }, t);
    },
  },
};
</script>

<style scoped lang="scss">
.container {
  position: fixed;
  z-index: 9999;
  left: 0;
  top: 20%;
  .dialog-box {
    position: relative;
    padding: 28rpx 56rpx;
    background-color: #fff;
    border-radius: 20rpx;
    .active-box {
      .num {
        line-height: 120rpx;
        color: #6672fc;
        font-size: 24rpx;
        width: 100%;
        text-align: center;
      }
    }
    .scroll-view {
      position: relative;
      width: 600rpx;
      height: 200rpx;
      white-space: nowrap;
      overflow: hidden;
      .active {
        position: absolute;
        z-index: 9;
        top: 20rpx;
        left: calc(300rpx - 2rpx);
        width: 8rpx;
        height: 56rpx;
        border-radius: 5rpx;
        background-color: #6672fc;
      }
      .box {
        position: absolute;
        z-index: 1;
        top: 43rpx;
        left: 0;
        padding: 0 300rpx;
      }
      .seize-seat {
        position: relative;
        display: inline-block;
        border-left: 4rpx solid #eee;
        border-top: 4rpx solid #eee;
        height: 30rpx;
        box-sizing: border-box;
        text {
          position: absolute;
          top: 43rpx;
          font-size: 20rpx;
          text-align: center;
          color: rgba(0, 0, 0, 0.4);
        }
        &:last-child {
          border-top: none;
        }
      }
    }
  }
}
</style>