Kaynağa Gözat

feat: 大转盘抽奖

Burt 2 yıl önce
ebeveyn
işleme
eca4c9f7c0

BIN
screenshots/lottery2.png


+ 7 - 0
src/pages.json

@@ -59,6 +59,13 @@
         "navigationBarTitleText": "九宫格抽奖"
       }
     },
+    {
+      "path": "pages/demo/lottery2",
+      "type": "page",
+      "style": {
+        "navigationBarTitleText": "大转盘抽奖"
+      }
+    },
     {
       "path": "pages/login/login",
       "type": "page",

BIN
src/pages/demo/lottery2-1.png


BIN
src/pages/demo/lottery2-2.png


+ 181 - 0
src/pages/demo/lottery2.vue

@@ -0,0 +1,181 @@
+<route lang="json5">
+{
+  style: { navigationBarTitleText: '大转盘抽奖' },
+}
+</route>
+
+<template>
+  <view class="mt-4 h-10 text-center">大转盘抽奖</view>
+  <div class="lottery-box">
+    <div class="lottery-list">
+      <div class="lottery-item" v-for="(n, index) in 1" :key="n">
+        <div class="lottery-item-inner">
+          <div class="lottery-item-gift">奖品{{ index + 1 }}</div>
+        </div>
+      </div>
+      <div ref="pointer" class="pointer" @click="handleClick">
+        <div>开始</div>
+        <div>抽奖</div>
+      </div>
+    </div>
+  </div>
+  <view class="leading-8">
+    <view class="mt-8 text-center text-green-600">下面是调试过程图片</view>
+    <view class="mb-8 text-center text-green-600">欢迎感兴趣的玩家继续优化</view>
+    <view class="text-center text-blue-600">计算lottery-item-inner节点的padding-left值</view>
+    <image src="./lottery2-1.png" mode="widthFix" class="w-full" />
+    <view class="text-center text-blue-600">调整lottery-item-gift节点</view>
+    <image src="./lottery2-2.png" mode="widthFix" class="w-full" />
+  </view>
+</template>
+
+<script lang="ts" setup>
+const giftLen = 8
+const deg = 360 / giftLen // 每份的角度
+const loop = 4 // 转多少圈,4圈
+const state = reactive({
+  lottery: 0, // 本次抽奖的奖品索引
+  lastLottery: 0, // 上一次抽奖的奖品索引
+  stopDeg: 0, // 最终要旋转的角度
+  loading: false,
+})
+
+const pointer = ref()
+
+function handleClick() {
+  if (state.loading) return
+  state.loading = true
+  // 最终获得的奖品索引,实际业务中是通过接口获取的,这里使用随机数0~9来模拟下
+  state.lottery = Math.floor(Math.random() * giftLen)
+  console.log(state.lottery)
+  // 最终的旋转角度,指针指向本次奖品的旋转角度+指针从上一次的奖品指向回归0的旋转角度+ 默认转动三圈
+  state.stopDeg += (state.lottery + (giftLen - state.lastLottery)) * deg + loop * 360
+  // 旋转
+  pointer.value.style.transform = `rotate(${state.stopDeg}deg)`
+
+  // uni不支持addEventListener所以改用下面的
+  setTimeout(() => {
+    state.lastLottery = state.lottery
+    state.loading = false
+    // alert(`恭喜获得奖品${state.lottery + 1}`)
+    uni.showModal({
+      title: `恭喜获得奖品${state.lottery + 1}`,
+    })
+  }, 3000)
+}
+
+// 旋转动画结束,弹出奖品
+// pointer.value.addEventListener('transitionend', () => {
+//   alert(`恭喜获得奖品${state.lottery + 1}`)
+//   // 保留奖品索引
+//   state.lastLottery = state.lottery
+//   state.loading = false
+// })
+</script>
+
+<style lang="scss">
+.lottery-box {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.lottery-list {
+  --size: 600rpx;
+  --half: calc(var(--size) / 2);
+  --len: 8; // 与代码一直
+  --deg: calc(360 / var(--len) * 1deg);
+  --deg-num: calc(360 / var(--len));
+
+  position: relative;
+  width: var(--size);
+  height: var(--size);
+  border: 2px solid #f55;
+  border-radius: 50%;
+}
+
+.lottery-item {
+  position: absolute;
+  top: 0;
+  left: var(--half);
+  width: var(--half);
+  height: var(--size);
+
+  // overflow: hidden; // 把这个注释掉可以看到最初的模样
+  background-color: #ff5350a1; // 放开这个可以看到最初的模样
+  transform-origin: left center;
+}
+
+.lottery-item-inner {
+  position: absolute;
+  top: 0;
+  left: calc(-1 * var(--half));
+  box-sizing: border-box;
+  width: var(--half);
+  height: var(--size);
+  padding-left: calc(((1 - sin(var(--deg-num))) * var(--size)));
+  font-size: 12px;
+  border-radius: var(--half) 0 0 var(--half);
+  transform: rotate(var(--deg));
+  transform-origin: right center;
+}
+
+.lottery-item-inner .lottery-item-gift {
+  display: block;
+  text-align: center;
+  transform: rotate(calc(-0.5 * var(--deg))) translateY(16px)
+    translateX(calc(0.5 * var(--half) * (1 - 1 / cos(0.5 * var(--deg)))));
+  transform-origin: center;
+}
+
+.lottery-item:nth-child(2n + 1) .lottery-item-inner {
+  background: #fef6e0a1;
+}
+
+.lottery-item:nth-child(2n) .lottery-item-inner {
+  background: #ffffffa1;
+}
+
+// TODO 与上面的--len一致
+@for $i from 1 through 8 {
+  .lottery-item:nth-child(#{$i}) {
+    transform: rotate(calc(($i - 1 - 0.5) * var(--deg)));
+  }
+}
+
+.pointer {
+  --pointer-size: 40px;
+  --pointer-padding: calc(var(--pointer-size) / 5);
+
+  position: absolute;
+  top: calc(var(--half) - var(--pointer-size) / 2 - var(--pointer-padding));
+  left: calc(var(--half) - var(--pointer-size) / 2 - var(--pointer-padding));
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  width: var(--pointer-size);
+  height: var(--pointer-size);
+  padding: var(--pointer-padding);
+  font-size: 12px;
+  text-align: center;
+  background-color: #ffffffd1;
+  border: 1px solid #ff5350;
+  border-radius: 50%;
+  transition: transform 3s cubic-bezier(0.2, 0.93, 0.43, 1);
+}
+
+.pointer::after {
+  --caret-size: 8px;
+
+  position: absolute;
+  bottom: calc(var(--pointer-size) + var(--pointer-padding) * 2);
+  left: calc(var(--pointer-size) / 2 - var(-caret-size) / 2);
+  content: '';
+  border-color: transparent;
+  border-style: solid;
+  border-width: calc(var(--caret-size) * 2) var(--caret-size);
+  border-bottom-color: #ff5350;
+  transform-origin: center;
+}
+</style>

+ 1 - 0
uni-pages.d.ts

@@ -7,6 +7,7 @@ interface NavigateToOptions {
   url: "pages/index/index" |
        "pages/demo/clock" |
        "pages/demo/lottery" |
+       "pages/demo/lottery2" |
        "pages/login/login" |
        "pages/my/index" |
        "pages/throughout/index" |