还你一个为所欲为的掘金抽奖

1,778 阅读5分钟

寒暄

掘金的抽奖系统已经出来一段时间了,不知道大伙都抽中了什么呢,还是像我这样经历了绝望,看破红尘,存起来留给下一代。

image.png

这种抽奖场景在活动中很常见,为了更好的摸鱼,决定自己去写一个插件来解决重复劳动。接下来为大伙介绍一个不错的宫格抽奖组件,请看官往下挪步

关于grid-roll

grid-roll是一个vue的宫格组件,它让ui和逻辑分离,封装了逻辑和宫格布局,让开发者只关注奖品和按钮的ui部分。

  • 自定义宫格数量,经典的3x3还是10x100都不在话下
  • 多抽功能,一次点击多次抽奖,谷底梭哈,就问你刺不刺激

安装

npm i grid-roll -S
yarn add grid-roll

引入

/** 引入 */
import { gridRoll, gridStart, gridPrize } from 'grid-roll'
import 'grid-roll/dist/grid-roll.min.css'

实践

通过vuecli搭起新项目,这边我们可以直接用掘金抽奖的图片链接,拿过来吧你。

图片上的奖品我都打上了数字记号,这些记号其实就奖品数组的下标,它们对应着奖品位置,布局从左到右一行一行排列,所以我们的奖品数组元素排序要注意下

image.png

通过使用grid-roll,我们只需要定义里面8个奖品和1个按钮的样式就行,用gridStart和gridPrize去包装这些物料,塞进gridRoll里面,gridRoll会帮我们自动调整成九宫格布局。这里,我更喜欢把奖品写成数据去循环生成gridPrize。然后样式布局基本是打开开发者工具复制掘金的样式,所以就不细说了

image.png

介绍下这3个组件:

  • gridRoll:interval这个属性用来定义宫格之前的间隔,默认是没有间隔的,这里我看感觉定义了6px。并且接受两个插槽button和prize
  • gridStart:专门用来做button插槽的组件
  • gridPrize:专门用来做prize插槽的组件
<gridRoll interval="6px">
    <!-- 按钮 -->
    <template v-slot:button>
      <gridStart>
        <div class="turntable-item item lottery">
          <div class="lottery-text">抽奖</div>
          <div class="text">200矿石1次</div>
        </div>
      </gridStart>
    </template>
    <!-- 奖品 -->
    <template v-slot:prize>
      <gridPrize
        v-for="prize in prizes"
        :key="prize.id"
        :pid="prize.id"
      >
        <div class="turntable-item item">
          <div class="image">
            <img :src="prize.img" alt="" />
          </div>
          <div class="text">{{ prize.text }}</div>
        </div>
      </gridPrize>
    </template>
  </gridRoll>
// 这里引入组件和样式
import { gridRoll, gridStart, gridPrize } from "grid-roll";
import "grid-roll/dist/grid-roll.min.css";
expoet default {
    data () {
        return {
          prizes: [
            {
              id: 1,
              text: "66矿石",
              img: "https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/32ed6a7619934144882d841761b63d3c~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
            },
            {
              id: 2,
              text: "随机限量徽章",
              img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/71c68de6368548bd9bd6c8888542f911~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
            },
            {
              id: 3,
              text: "掘金新款T恤",
              img: "https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5bf91038a6384fc3927dee294a38006b~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
            },
            {
              id: 4,
              text: "Bug",
              img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0a4ce25d48b8405cbf5444b6195928d4~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
            },
            {
              id: 5,
              text: "再抽2次解锁",
              img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aabe49b0d5c741fa8d92ff94cd17cb90~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
            },
            {
              id: 6,
              text: "掘金限量桌垫",
              img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c78f363f41a741ffa11dcc8a92b72407~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
            },
            {
              id: 7,
              text: "Yoyo抱枕",
              img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/33f4d465a6a9462f9b1b19b3104c8f91~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
            },
            {
              id: 8,
              text: "再抽3次解锁",
              img: "https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4decbd721b2b48098a1ecf879cfca677~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
            },
          ],
      }
    }
    components: {
        gridRoll,
        gridStart,
        gridPrize,
    },
}

从上面可以看到,我们只需要通过gridStart和gridPrize定义好按钮和奖品的样式,放进gridRoll就行,不用再去管其他乱七八糟的操作。

disabled的使用

从官方的图看起来,这边还缺少一个“锁”样式,需要通过抽奖次数进行解锁,除了奖品样式的不同,在滚动的时候还会直接跳过未解锁的奖品。这边gridPrize也有一个对应的prop做这件事。

首先在prizes需要用到“锁”的元素中添加一个字段disabled: true,传给gridPrize,当抽奖开始的时候,滚动会直接跳过disabled为true的奖品,其次我们用disabled来做一些样式区分,这里样式也是照抄掘金

<gridPrize
                    v-for="prize in prizes"
                    :key="prize.id"
                    :pid="prize.id"
                    :disabled="prize.disabled"
                  >

image.png

这里我们基本就完成静态样式啦,接下来就是说说怎么触发这个抽奖

抽奖

抽奖的行为是由gridPrize的startRoll函数提供的,这里通过ref获取gridRoll的实例,定义一个handleLottery方法用来触发startRoll函数。再把handleLottery绑定的抽奖按钮上

<gridRoll interval="6px" ref="dial">
<!-- 绑定事件 -->
<gridStart>
    <div
      @click="handleLottery"
      class="turntable-item item lottery"
    >
methods: {
    async handleLottery() {
      const value = 1;
      /**
       * 这里的value为1是指抽取id为1的奖品
       * 返回一个Promise实例,内部为了防止多次触发抽奖逻辑,
       * resolve会传递一个Boolean,进行是false,抽奖结束返回true
       */
      const b = await this.$refs.dial.startRoll(value);
      if (b) {
        alert(
          `🎉你抽到${this.prizes.find((prize) => prize.id === value).text}`
        );
      } else {
        console.warn("稍安勿躁");
      }
    },
  },

同时别忘记了,抽奖滚动的时候,有一个选中的样式,这里gridPrize作用域插槽提供了一个isSelect值用来判断是否滚动到当前奖品,用来做一些样式切换

<!-- disabled为true的话,跳过 -->
<gridPrize
    v-for="prize in prizes"
    :key="prize.id"
    :pid="prize.id"
    :disabled="prize.disabled"
  >
  <!-- isSelect是用来判断是否滚动到当前奖品 -->
    <template v-slot="{ isSelect }">

九宫格抽奖已经可以运行起来了(gif掉帧问题,想看完整demo可点击结尾链接

未命名.gif

奖品解锁

现在的九宫格无论你抽了多少次,右面的那两个还是处于被锁状态,所以需要进行一个小改动

  1. 维护一个completeNumber来统计抽取次数
  2. 把data的prizes移动到计算属性里面
  3. handleLottery抽取完成后completeNumber++
// 整个js的具体代码,抽奖方法简单几句代码便实现
export default {
  name: "App",
  data() {
    return {
      completeNumber: 0,
    };
  },
  computed: {
    prizes() {
      return [
        {
          id: 1,
          text: "66矿石",
          img: "https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/32ed6a7619934144882d841761b63d3c~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
        },
        {
          id: 2,
          text: "随机限量徽章",
          img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/71c68de6368548bd9bd6c8888542f911~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
        },
        {
          id: 3,
          text: "掘金新款T恤",
          img: "https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5bf91038a6384fc3927dee294a38006b~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
        },
        {
          id: 4,
          text: "Bug",
          img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0a4ce25d48b8405cbf5444b6195928d4~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
        },
        {
          id: 5,
          text:
            this.completeNumber >= 2
              ? "乐高海洋巨轮"
              : `再抽${2 - this.completeNumber}次解锁`,
          img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aabe49b0d5c741fa8d92ff94cd17cb90~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
          disabled: this.completeNumber < 2,
        },
        {
          id: 6,
          text: "掘金限量桌垫",
          img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c78f363f41a741ffa11dcc8a92b72407~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
        },
        {
          id: 7,
          text: "Yoyo抱枕",
          img: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/33f4d465a6a9462f9b1b19b3104c8f91~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
        },
        {
          id: 8,
          text:
            this.completeNumber >= 3
              ? "Switch"
              : `再抽${3 - this.completeNumber}次解锁`,
          img: "https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4decbd721b2b48098a1ecf879cfca677~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp",
          disabled: this.completeNumber < 3,
        },
      ];
    },
  },
  components: {
    gridRoll,
    gridStart,
    gridPrize,
  },
  methods: {
    async handleLottery() {
      const value = 1;
      const b = await this.$refs.dial.startRoll(value);
      if (b) {
        alert(
          `🎉你抽到${this.prizes.find((prize) => prize.id === value).text}`
        );
        this.completeNumber++;
      } else {
        console.warn("稍安勿躁");
      }
    },
  },
};

小tips

在实际业务情况下,想必每次抽奖都会先调接口去拿到抽奖信息,获取信息后才进行startRoll,为了更好的交互,gridPrize这边提供了一个continueRoll方法,用来启动滚动,而不需要等待接口调用完才开始抽奖。

结语

从本文可以看到,grid-roll的出现,让我们只需要关注奖品按钮的样式和奖品数据及一个简单的抽奖函数,并且这只是基础功能,还有更多其他功能具体可以看下方文档。demo里面每次只转id为1的奖品太枯燥了,所以我还添加了一个自定义抽奖的功能,方便大家幕后操纵

在线 demo page 预览

项目地址

grid-roll 地址