基于vue3的九宫格抽奖组件

2023-10-08 22:17:56发布
27

案例下载地址: https://download.csdn.net/download/QQ408896436/87190628


Lottery.vue

<template>
  <div class="lottery">
    <img :src="config.bg" />
    <span ref="lightBtnRef" class="lightBtn" v-show="isShowLightBtn">
      <img :src="config.lighturl" :width="config.boxw" :height="config.boxh"/>
    </span>
  </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
const props = defineProps({
  config: {
    type: Object,
    default() {
      return {
        bg: "", // 抽奖bg
        lighturl: "", // 转动的图片url
        total: 0, // 总共有多少奖品
        boxw: 0, // 转动图片的宽度
        boxh: 0, // 转动图片的高度
        pos: "", // 坐标
      };
    },
  },
});

const posArr = props.config.pos.split(","); // 转动图片的位置数组
const isShowLightBtn = ref(false); // 是否显示lightBtn
const isLotterIng = ref(false); // 标记是否正在抽奖
const nowIndex = ref(0); // lightBtn当前的位置
const stayIndex = ref(0); // 抽中的奖品下标
const runStep = ref(0); // 需要转多少次才开始体质
const flag = ref(true);
const lightBtnRef = ref("");
const $emit = defineEmits(["end"]);

// 获取lightBtn的位置
const getPos = () => {
  nowIndex.value++;

  // 转完一圈
  if (nowIndex.value >= props.config.total) {
    nowIndex.value = 0;
    runStep.value++;
  }

  // 可以开始停止转盘了(不是马上停止)
  if (
    runStep.value >= (props.config.maxRunStep || 3) &&
    nowIndex.value === stayIndex.value
  ) {
    runStep.value = props.config.maxRunStep || 3;
    flag.value = false;
  }

  // 返回坐标
  const nowPos = posArr[nowIndex.value];
  const x = nowPos.split("_")[0];
  const y = nowPos.split("_")[1];

  return {
    x,
    y,
  };
};

// 设置lightBtn的坐标
const setPos = () => {
  const p = getPos();
  lightBtnRef.value.style.left = p.x + "px";
  lightBtnRef.value.style.top = p.y + "px";
};

// 抽奖逻辑
const run = () => {
  isShowLightBtn.value = true;

  let speed = 50;
  let id = 0;
  let t = 0;

  id = window.setInterval(() => {
    // 由慢到快
    if (flag.value) {
      speed -= 0.1;
    } else {
      speed += 0.1;
    }

    // 间隔最小为10
    if (speed <= 10) {
      speed = 10;
    }

    // 间隔最大为100
    if (speed >= 100) {
      speed = 100;

      // 停止转动
      if (nowIndex.value === stayIndex.value) {
        clearInterval(id);
        $emit("end", nowIndex.value);

        // 属性重置
        runStep.value = 0;
        flag.value = true;
        isLotterIng.value = false;
        id = 0;
      }
    }

    t++;

    // 不让速度过快
    if (t >= speed) {
      setPos();
      t = 0;
    }
  }, 0);
};

// 开始抽奖
const start = (stopIndex) => {
  if (isLotterIng.value) {
    return;
  }
  isLotterIng.value = true;
  stayIndex.value = stopIndex;
  run();
};

onMounted(() => {
  lightBtnRef.value.style.left = posArr[0].split("_")[0] + "px";
  lightBtnRef.value.style.top = posArr[0].split("_")[1] + "px";
});

// 暴露start方法
defineExpose({
  start,
});
</script>
<style scoped>
.lottery {
  position: relative;
}
.lottery .btn {
  position: relative;
  display: inline-block;
  cursor: pointer;
}
.lottery .lightBtn {
  position: absolute;
}
</style>

使用案列

<template>
  <div class="app">
    <Lottery ref="lotteryRef" :config="lyConfig" @end="end" />
    <span
      :class="[isLotterIng ? 'lotterycontent_disable' : 'lotterycontent_start']"
      @click="lottery"
    ></span>
  </div>
</template>
<script setup>
import { reactive, ref } from "vue";
import picBg from "@/assets/img/bg.png";
import picLightUrl from "@/assets/img/light.png";
import Lottery from "@/components/Lottery.vue";

// 配置
const lyConfig = reactive({
  bg: picBg,
  lighturl: picLightUrl,
  total: 12,
  boxw: 154,
  boxh: 156,
  maxRunStep: 4,
  pos: "157_0,313_0,469_0,625_0,781_156,781_312,626_467,470_467,314_467,158_467,1_312,1_156",
});

const lotteryRef = ref("");

const isLotterIng = ref(false);

// 调用抽奖方法
const lottery = () => {
  isLotterIng.value = true;
  lotteryRef.value.start(parseInt(Math.random() * 12)); // 这里输入中间的数组下标,实际情况是用后端返回的数字
};

// 抽奖完毕后的回调函数
const end = (index) => {
  isLotterIng.value = false;
  alert(`奖品位置在${index}`);
};
</script>
<style scoped>
.app {
  position: relative;
}
.app .lotterycontent_start {
  position: absolute;
  width: 205px;
  height: 54px;
  left: 179px;
  top: 398px;
  display: block;
  outline: none;
  cursor: pointer;
  background: url("@/assets/img/blankstartbtn.png") no-repeat 0 0;
  backface-visibility: hidden;
  animation: 1s ease 0s infinite alternate none running
    lotterycontent_borderLight;
}
.app .lotterycontent_disable {
  position: absolute;
  width: 205px;
  height: 54px;
  left: 179px;
  top: 398px;
  display: block;
  outline: none;
  background: rgba(0, 0, 0, 0.5);
  cursor: default;
}
@keyframes lotterycontent_borderLight {
  0% {
    box-shadow: 0px 0px 10px 10px rgba(255, 255, 255, 0.3) inset;
  }
  100% {
    box-shadow: 0px 0px 20px 20px rgba(255, 255, 255, 0.6) inset;
  }
}
</style>

运行效果