uni-app 老虎机功能

MuYan2022-09-19VueVueuni-app
  • 该老虎机抽奖功能兼容 小程序/APP/H5,主要通过 swiper 内置组件实现

组件配置

  • 子组件
<!-- m-slotMachine-item.vue -->
<template>
  <view class="draw-swiper-wrap">
    <swiper class="draw-swiper" skip-hidden-item-layout circular disable-touch acceleration :indicator-dots="false"
      :vertical="true" :display-multiple-items="1" :current="currentIndex" :autoplay="isAutoplay"
      :interval="interval" :duration="duration" @change="onChange">
      <swiper-item v-for="(item, index) in prizeArr" :key="index">
        <image class="draw-prize-img" mode="aspectFill" :src="item.img"></image>
      </swiper-item>
    </swiper>
  </view>
</template>

<script>
  export default {
    props: {
      index: { // 结果下标
        type: Number,
        default: 0
      },
      prizeArr: { // 奖品数组
        type: Array,
        default: () => {
          return [];
        }
      },
      interval: { // 自动切换时间间隔
        type: Number,
        default: 0
      },
      duration: { // 滑动动画时长
        type: Number,
        default: 0
      },
    },
    data() {
      return {
        isAutoplay: false, // 是否开始抽奖 (自动播放)
        currentIndex: 0, // swiper对应的index
      }
    },
    methods: {
      onChange(event) {
        if (this.index === event.detail.current) {
          this.isAutoplay = false;
          // 抽奖结束
          setTimeout(() => {
            this.$emit("drawStop")
          }, 500)
        }
      },
      //触发滚动
      startScroll() {
        this.isAutoplay = true;
      }
    },
  }
</script>

<style lang="scss" scoped>
  .draw-swiper-wrap {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    flex: 1;

    .draw-swiper {
      width: 100%;
      height: 100%;
    }

    .draw-prize-img {
      width: 100%;
      height: 100%;
      border-radius: 10rpx;
    }
  }
</style>
  • 父组件
<!-- m-slotMachine.vue -->
<template>
  <view class="m-slotMachine-wrap">
    <view class="m-slotMachine">
      <m-slot-machine-item :ref="`slotMachine${index}`" :interval="50" :duration="100" :prizeArr="prizeArr"
        :index="results[index]" v-for="(item, index) in column" :key="index" @drawStop="stop(index)" />
    </view>
  </view>
</template>

<script>
  import mSlotMachineItem from './m-slotMachine-item.vue';
  export default {
    components: {
      mSlotMachineItem
    },
    props: {
      // 抽奖秒数
      seconds: {
        type: Number,
        default: 3
      },
      // 每个奖池滚动间隔
      interval: {
        type: Number,
        default: 200
      },
      // 奖池
      prizeArr: {
        type: Array,
        default: () => {
          // 数组内容解析,奖池图片的默认尺寸为 80 * 120
          // {
          //   name: '奖品 名称'
          //   value: '奖品 ID'
          //   img: '图片 url 地址'
          // }
          return []
        }
      },
      // 抽奖列数
      column: {
        type: Number,
        default: 3
      }
    },
    data() {
      return {
        luckyDrawLoading: false, // 抽奖中
        results: [-1,-1,-1], // 开奖结果
      }
    },
    watch: {
      prizeArr(newVal) {
        // 初始化随机
        this.$nextTick(() => {
          this.results = this.getResults()
        })
      }
    },
    methods: {
      // 开始抽奖
      start(id = undefined) {
        if (this.luckyDrawLoading) {
          return;
        }
        // 防止重复点击
        this.luckyDrawLoading = true;
        // 初始化结果
        this.results = [-1,-1,-1]
        this.results.forEach((item, index) => {
          // 开始滚动
          setTimeout(() => {
            this.$refs[`slotMachine${index}`][0].startScroll();
          }, (index * interval));
        })
        let {
          interval,
          seconds
        } = this
        let getArr = this.getResults(id)
        this.results.forEach((item, index) => {
          // 开始滚动
          setTimeout(() => {
            this.$set(this.results, index, getArr[index])
          }, (seconds * 1000) + (index * interval));
        })
      },
      // 抽奖结束
      stop(index) {
        if(index !== this.results.length - 1) {
          return
        }
        this.luckyDrawLoading = false;
        this.$emit('drawEnd')
      },
      // 获取结果 or 随机
      getRandom(arr = []) {
        let max = this.prizeArr.length - 1;
        let index = Math.floor(Math.random() * (max - 1 + 1) + 1)
        if (!arr.includes(index)) {
          return index
        } else {
          return this.getRandom(arr)
        }
      },
      // 获取中奖结果下标数组
      getRandomArr(id = undefined) {
        let results = [];
        for(let i = 0;i < this.column;i++) {
          results.push(
            id ?
            this.prizeArr.findIndex(item => item.value === id) :
            this.getRandom(results)
          )
        }
        return results
      },
      // 获取中奖结果
      getResults(id = undefined) {
        if(!this.prizeArr.length){
          return uni.showToast({
            title: '奖池不能为空',
            duration: 2000,
            icon: 'none'
          });
        }
        // id 为中奖
        let arr = this.getRandomArr(id);
        return arr
      },
    },
  }
</script>

<style lang="scss" scoped>
  .m-slotMachine-wrap{
    width: 558rpx;
    height: 280rpx;
    .m-slotMachine {
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: space-between;
      position: relative;
      &::before {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0);
        z-index: 6;
        content: '';
      }
    }
  }
</style>

使用

<template>
  <view>
    <view class="draw_wrap">
      <m-slotMachine ref="draw" :prizeArr="prizeArr" @drawEnd="end"/>
    </view>
    <button @click="drawClick()">抽奖</button>
  </view>
</template>

<script>
  import mSlotMachine from "@/components/m-slotMachine/m-slotMachine.vue";
  export default {
    components: {
      mSlotMachine
    },
    data() {
      return {
        prizeArr: [],
      }
    },
    onLoad() {
      this.init()
    },
    methods: {
      init() {
        // 初始化获取奖池信息
        this.prizeArr = [
          {
            name: 'iPhone13',
            value: 'iPhone',
            img: require('@/xxx.png')
          },
          {
            name: 'airPods3',
            value: 'airPods',
            img: require('@/xxx.png')
          },
          {
            name: '行李箱',
            value: 'luggage',
            img: require('@/xxx.png')
          },
          {
            name: '风筒',
            value: 'dryer',
            img: require('@/xxx.png')
          },
          {
            name: '平行车',
            value: 'balanceCar',
            img: require('@/xxx.png')
          },
          {
            name: 'iPad5',
            value: 'iPad',
            img: require('@/xxx.png')
          }
        ]
      },
      drawClick(){
        // 模拟抽奖结果,随机一个奖品,实际可根据后端返回的奖品 ID 进行配置
        let id = this.$refs.draw.prizeArr[this.$refs.draw.getRandom()].value
        this.$refs.draw.start(id);
      },
      end() {
        console.log('抽奖结束')
      }
    },
  }
</script>

<style lang="scss" scoped>
  page {
    background: #fff;
    padding: 0 20rpx;
  }
  .draw_wrap{
    display: flex;
    justify-content: center;
    padding: 80rpx 0;
    ::v-deep{
      .draw-swiper-wrap{
        width: 160rpx;
        height: 240rpx;
        border-radius: 10rpx;
        border: 2rpx solid #d2d2d2;
        overflow: hidden;
        & + .draw-swiper-wrap{
          margin-left: 20rpx;
        }
      }
    }
  }
</style>
上次更新 2026/6/23 11:49:15
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8
本页目录