fundo
Há um ditado que diz: "Quando você tem um martelo na mão, vê pregos em todo o mundo". Seguindo o desenvolvimento desenvolveu um componente de lista virtual com uma altura fixa de itens de lista, e o componente de desenvolvimento tornou-se viciado. No projeto recente, uma animação de loteria giratória de nove quadrados deve ser realizada. Parece que esta função pode ser reutilizada. Também se destina a ser feito em um componente. Com o paradigma de desenvolvimento explorado no último componente de desenvolvimento, o componente de desenvolvimento desta vez tem menos problemas do que o componente de desenvolvimento. Agora vamos ao assunto de hoje.
Demonstração de efeito
Primeiro, observe o efeito final. Depois de clicar no botão da loteria, a loteria começa a girar. A animação da loteria muda de lenta para rápida e depois mantém uma velocidade constante. Quando ela para, ela fica mais lenta, o que é mais adequado para o efeito de rotação de objetos do mundo real. A propósito, a função de loteria neste artigo é desenvolvida vue3 + vite + ts
adotando o método de desenvolvimento.Se você usar outras pilhas de tecnologia, precisará modificá-la para usá-la.
Ideias de implementação
A animação de sorteio de loteria Jiugongge é o núcleo da função de sorteio de loteria Jiugongge. Conforme mostrado na figura abaixo: Normalmente, a animação do letreiro Jiugongge gira no sentido horário e a sequência de rotação é [1, 2, 3, 6, 9, 8, 7, 4]. Ao virar para qual grade, adicione um estilo de realce a esta grade e parece que tem um efeito cintilante. Além disso, a velocidade de rotação irá variar. No início, ele acelera da velocidade inicial para a velocidade mais rápida, e depois continua girando em velocidade constante. Ao atingir a grade vencedora, a velocidade deve ser reduzida. Como controlar a velocidade de rotação da marquise? Pode ser julgado com base no número de etapas para alcançar a posição atual e no número total de etapas que precisam ser giradas. Por exemplo, no primeiro terço da jornada, deixe o cronômetro acelerar e, no último sexto da jornada, deixe o cronômetro acelerar e mantenha uma velocidade constante no meio da jornada e defina o valor do tempo de o temporizador para um valor fixo.
Faça Você Mesmo
Desenhe uma página estática
Encontrei três fotos na Internet, ou seja, a foto de ouro com desconto instantâneo de 10 yuans, obrigado por participar da foto e a foto do botão de loteria. Use essas três fotos para gerar uma lista de dados de material de 9 grades. Por que usar 9 imagens em vez de uma imagem inteira? A principal razão é que, se você usar uma imagem inteira, o efeito de realce não é fácil de obter ao definir a grade pela qual a marquise passa.
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>
结语
Este é o fim da realização dos principais pontos de função da versão vue3+vite4+ts do sorteio rotativo da loteria Jiugongge. Se você deseja obter o código completo do exemplo deste artigo, clique aqui para baixá-lo . Além disso, este componente também foi carregado no site oficial do npm, você pode clicar aqui para visualizar , instalar e usar.