Teach you how to develop a Jiugongge lottery animation

background

There is a saying that goes, "When you have a hammer in your hand, you see nails all over the world." Following the development developed a virtual list component with a fixed height of list items, and the development component became addicted. In the recent project, a nine-square turntable lottery animation is to be realized. It seems that this function can be reused. It is also intended to be made into a component. With the development paradigm explored in the last development component, the development component this time has less fetters of problems other than the development component. Now let's get to today's topic.

Effect demonstration

First look at the final effect. After clicking the lottery button, the lottery starts to rotate. The animation of the lottery changes from slow to fast, and then maintains a constant speed. When it stops, it slows down, which is more suitable for the rotation effect of real-world objects. By the way, the lottery function in this article is developed vue3 + vite + tsby adopting the development method. If you use other technology stacks, you need to modify it to use it.

channel.gif

Implementation ideas

Jiugongge lottery draw marquee animation is the core of Jiugongge lottery draw function. As shown in the figure below: Usually the Jiugongge marquee animation rotates clockwise, and the rotation sequence is [1, 2, 3, 6, 9, 8, 7, 4]. When turning to which grid, add a highlight style to this grid, and it looks like it has a flickering effect. In addition, the speed of rotation will vary. At the beginning, it accelerates from the initial speed to the fastest speed, and then keeps rotating at a constant speed. When it reaches the winning grid, the speed should be reduced. How to control the rotation speed of the marquee? It can be judged based on the number of steps to reach the current position and the total number of steps that need to be turned. For example, in the first third of the journey, let the timer accelerate, and in the last one-sixth of the journey, let the timer accelerate, and keep a constant speed in the middle of the journey, and set the timing value of the timer to a fixed value.

image.png

Do it yourself

Draw a static page

I found three pictures from the Internet, namely the 10 yuan instant discount gold picture, thank you for participating in the picture, and the lottery button picture. Use these three pictures to generate a 9-grid material data list. Why use 9 pictures instead of a whole picture? The main reason is that if you use a whole picture, the highlighting effect is not easy to achieve when setting the grid that the marquee passes through.


  import { reactive } from 'vue';
  import { NineGridLottery } from '@lib/core';
  import RMBImg from '@/assets/10rmb.png';
  import LotteryBtnImg from '@/assets/lottery-btn.png';
  import ThankImg from '@/assets/thank.png';

  // 奖品列表
  const lotteryList = reactive([
    { name: '10元立减金', pic: RMBImg },
    { name: '谢谢参与', pic: ThankImg },
    { name: '10元立减金', pic: RMBImg },
    { name: '谢谢参与', pic: ThankImg },
    { name: '抽奖', pic: LotteryBtnImg },
    { name: '谢谢参与', pic: ThankImg },
    { name: '10元立减金', pic: RMBImg },
    { name: '谢谢参与', pic: ThankImg },
    { name: '10元立减金', pic: RMBImg },
  ]);

网页结构如下: index=4 时,这个位置是抽奖按钮,抽奖按钮一般交互比较多,所以用插槽的方式暴露给父组件,让父组件去定制。因为slot上不能绑定事件,所以需要在外层加个div,把点击事件绑定在外层的div上。考虑到不同的抽奖活动外观展示都不一样,所以抽奖的样式要允许父组件自定义。这里把抽奖组件的样式类名前缀交给父组件去配置。

<template>
  <div :class="`${props.classPrefix}-box`">
    <div
      v-for="(item, index) in props.lotteryList"
      :class="[`${props.classPrefix}-item`, { active: state.curIndex === index }]"
    >
      <template v-if="index !== 4">
        <img v-if="item.pic" :src="item.pic" class="pic" alt="" />
        <!-- <p class='text'>{{ item.name }}</p> -->
      </template>
      <div v-else @click="start()">
        <slot name="lotteryBtn" :itemData="item"> </slot>
      </div>
    </div>
  </div>
</template>

九宫格抽奖组件的样式是在父组件定义的,九宫格抽奖容器采用flex布局,需要注意的是 flex-wrap默认属性是不换行的,要将属性值设置成wrap。另外下载的png图片不太标准,有色彩的部分并不在png图片背景的正中央,需要向左偏移1px, 看起来左右才对称。转动高亮的效果使用区别于静止状态的背景图和阴影效果实现。

<style lang="less">
  .lottery-box {
    width: 375px;
    height: 375px;
    background: #ea0019;
    margin: 100px auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
    align-items: center;
    .lottery-item {
      width: 125px;
      height: 125px;
      position: relative;

      &.active {
        box-shadow: 2px 2px 30px #ffe4c0;
        background-color: #ffe4c0;
      }

      &:nth-of-type(5) {
        cursor: pointer;
      }

      .pic {
        width: 100%;
        height: 100%;
        position: absolute;
        left: -1px;
      }

      .text {
        width: 100%;
        height: 20px;
        background: rgba(0, 0, 0, 0.5);
        color: #fff;
        font-size: 12px;
        text-align: center;
        line-height: 20px;
        position: absolute;
        left: 0;
        bottom: 0;
      }
    }
  }
</style>

让抽奖转盘转起来

首先确定一下九宫格转盘的跑动顺序。顺时针转动时,每个格子的下标为:

 // 跑动顺序
  const lotterySort = [0, 1, 2, 5, 8, 7, 6, 3];

每跑一圈,需要跑八步。到达中奖格子位置跑马灯动画需要跑动的总步数=基本圈数*8+中奖格子在跑动数组中的位置。中奖的id在未跑动前,要先调用后端接口,确定下来。

  // 总执行步数
  const totalSteps = computed(() => {
    return props.baseCircles * 8 + lotterySort.indexOf(props.winId);
  });

跑动时,需要动态设置跑动速率。每次跑动时,清除上一次的速率。每前进一步,重新创建定时器,调整速率。并对到达位置的格子进行高亮显示。当跑动的步数大于等于总步数时,给父组件发消息。

 const timer: Ref<ReturnType<typeof setTimeout> | null> = ref(null);
 const startRun = () => {
    // 延时器的速度要动态调节
    timer.value && clearTimeout(timer.value);

    // console.log(`已走步数=${state.curStep}, 执行总步数=${totalSteps.value}`);

    // 已走步数超过要执行总步数, 则停止
    if (state.curStep >= totalSteps.value) {
      state.isRunning = false;
      return emit('end');
    }

    // 高亮抽奖格子序号
    state.curIndex = lotterySort[state.curStep % 8];
    // 速度调整
    state.speed = calcSpeed(state.speed);

    timer.value = setTimeout(() => {
      state.curStep++;
      startRun();
    }, state.speed);
  };

跑动速率的计算方法,前三分之一的路程,从初始速率加速到最快速率,然后保持最快速率,匀速跑动,到了总路程最后的六分之一时,开始减速,犹如刹车一样,慢慢停下来。

  // 需要加速的前段步数
  const frontSteps = Math.floor(props.baseCircles * 8 * (1 / 3));
  // 需要减速的后段步数
  const midSteps = Math.floor(totalSteps.value * (5 / 6));

  // 计算速度
  const calcSpeed = (speed: number) => {
    // 最快最慢速度
    const { fastSpeed, slowSpeed } = props;
    // 前段加速,中段匀速,后段减速
    if (state.curStep < frontSteps && speed > fastSpeed) {
      speed = speed - Math.floor((props.initSpeed - fastSpeed) / frontSteps);
    } else if (state.curStep > midSteps && speed < slowSpeed) {
      speed = speed + Math.floor((slowSpeed - fastSpeed) / frontSteps / 5);
    }
    return speed;
  };

使用方法

组件有 7 个配置参数:

组件属性名 含义
lotteryList 抽奖列表
winId 中奖 id
classPrefix 九宫格容器和奖项的样式类名前缀
initSpeed 初始转动速度 单位 ms (可选)
baseCircles 基本转动圈数 (可选)
fastSpeed 最快转动速度 单位 ms (可选)
slowSpeed 最慢转动速度 单位 ms (可选)
@end 转动结束事件
<template>
  <NineGridLottery
    :lotteryList="lotteryList"
    :winId="options.winId"
    :initSpeed="options.initSpeed"
    :baseCircles="options.baseCircles"
    @end="handleEnd"
    classPrefix="lottery"
  >
    <template #lotteryBtn="{ itemData }">
      <img :src="itemData.pic" class="pic" alt="" />
    </template>
  </NineGridLottery>
</template>

<script setup lang="ts">
  import { reactive } from 'vue';
  import { NineGridLottery } from '@lib/core';
  import RMBImg from '@/assets/10rmb.png';
  import LotteryBtnImg from '@/assets/lottery-btn.png';
  import ThankImg from '@/assets/thank.png';

  // 奖品列表
  const lotteryList = reactive([
    { name: '10元立减金', pic: RMBImg },
    { name: '谢谢参与', pic: ThankImg },
    { name: '10元立减金', pic: RMBImg },
    { name: '谢谢参与', pic: ThankImg },
    { name: '抽奖', pic: LotteryBtnImg },
    { name: '谢谢参与', pic: ThankImg },
    { name: '10元立减金', pic: RMBImg },
    { name: '谢谢参与', pic: ThankImg },
    { name: '10元立减金', pic: RMBImg },
  ]);

  // 后台配置的奖品数据
  const options = reactive({
    // 中奖id
    winId: 6,
    // 基本圈数 
    baseCircles: 4,
    // 抽奖转动速度
    initSpeed: 300,
    fastSpeed: 100,
    slowSpeed: 600,
  });

  const handleEnd = () => {
    alert('恭喜你中奖了');
  };
</script>

// 样式参照上文
<style lang="less"></style>

结语

This is the end of the realization of the main function points of the vue3+vite4+ts version of Jiugongge rotating lottery draw. If you want to get the complete code of the example in this article, please click here to download it . In addition, this component has also been uploaded to the npm official website, you can click here to view , install and use.

Guess you like

Origin juejin.im/post/7248168880572694588