vue3.0+TypeScript实现卡片九宫格选中后卡片翻转特效
API:
参数 | 说明 | 类型 | 默认值 |
---|
intervalTime | 卡片抽奖间隔时间 | number | |
delayTime | 中奖延迟显示时间 | number | |
priceWidth | 抽奖卡片的宽度 | number | |
priceHeight | 抽奖卡片的高度度 | number | |
circleTime | 抽奖几次之后中奖 | number | |
circleNum | 抽奖转动的次数 | number | |
priceId | 中奖ID(需要后台返回) | number | |
抽奖数据API:
参数 | 说明 | 类型 | 默认值 |
---|
id | 抽奖ID | number | 无 |
info | 中奖信息 | string | 无 |
front | 中奖面图片地址 | string | 无 |
back | 抽奖面图片地址 | string | 无 |
代码示例(父组件):
父组件
<template>
<Lottery :priceList="priceList"></Lottery>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import Lottery from "./../compoments/lottery.vue";
onMounted(() => {
// getSlideEle()
});
// 左侧浮窗轮播列表
const bargainUserVosList = ref([
"1恭喜书友123***5464获得《生命的真相》实体书5折券一张",
"2恭喜书友123***5464获得《生命的真相》实体书5折券一张",
"3恭喜书友123***5464获得《生命的真相》实体书5折券一张",
"4恭喜书友123***5464获得《生命的真相》实体书5折券一张",
"5恭喜书友123***5464获得《生命的真相》实体书5折券一张",
"6恭喜书友123***5464获得《生命的真相》实体书5折券一张",
"7恭喜书友123***5464获得《生命的真相》实体书5折券一张",
]);
interface priceInfo {
// 抽检间隔时间
intervalTime: number;
// 中奖延迟显示时间
delayTime: number;
// 中间显示奖品宽度
priceWidth: number;
// 中间显示奖品高度
priceHeight: number;
// 抽奖次数
circleTime: number;
// 抽奖循环次数
circleNum: number;
// 中奖Id
priceId: number;
}
interface listInfo {
id?: number;
front?: string;
back?: string;
info?: string;
}
const priceList: listInfo[] = [
{
id: 1,
info: "1元优惠券",
front: "https://XXX.com/activity/smile/is_all.png",
back: "https://XXX.com/activity/smile/all.png",
},
{
id: 2,
info: "10元优惠券",
front: "https://XXX.com/activity/smile/is_person.png",
back: "https://XXX.com/activity/smile/person.png",
},
{
id: 3,
info: "谢谢惠顾",
front: "https://XXX.com/activity/smile/is_good.png",
back: "https://XXX.com/activity/smile/good.png",
},
{
id: 4,
info: "豪华电动车",
front: "https://XXX.com/activity/smile/is_bless.png",
back: "https://XXX.com/activity/smile/bless.png",
},
{
id: 5,
info: "1w购物券",
front: "https://XXX.com/activity/smile/is_agency.png",
back: "https://XXX.com/activity/smile/agency.png",
},
];
const priceOptions = ref<priceInfo>({
// 抽检间隔时间
intervalTime: 200,
// 中奖延迟显示时间
delayTime: 500,
// 中间显示奖品宽度
priceWidth: 100,
// 中间显示奖品高度
priceHeight: 140,
// 抽奖次数
circleTime: 3,
// 抽奖循环次数
circleNum: 20,
// 中奖Id
priceId: 1,
});
</script>
代码示例(子组件):
<template>
<div class="lottery-container" id="lottery-container"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, defineProps } from "vue";
const drawOrder: any = ref([]); //抽奖书序
const currentIndex = ref<number | null>(null); // 当前选中的奖品
const count = ref(0); //抽奖次数
const isDrawing = ref(false); //是否在抽奖
const circle = ref(20);
const lotteryId = ref(1);
const priceObj = ref<priceInfo>({
intervalTime: 500,
delayTime: 200,
priceWidth: 100,
priceHeight: 140,
circleTime: 3,
circleNum: 20,
priceId: 1,
});
interface listInfo {
id?: number;
front?: string;
back?: string;
info?: string;
}
interface priceInfo {
// 抽检间隔时间
intervalTime: number;
// 中奖延迟显示时间
delayTime: number;
// 中间显示奖品宽度
priceWidth: number;
// 中间显示奖品高度
priceHeight: number;
// 抽奖次数
circleTime: number;
// 抽奖循环次数
circleNum: number;
// 中奖Id
priceId: number;
}
const props = defineProps({
priceParameter: {
type: Object as () => priceInfo,
default: () => ({
intervalTime: 50,
delayTime: 200,
priceWidth: 100,
priceHeight: 140,
circleTime: 3,
circleNum: 20,
priceId: 1,
}),
},
priceList: {
type: Array<listInfo>,
default: () => [],
},
});
onMounted(() => {
customOrder();
createDiv();
conductLottery();
});
// 创建所需要的抽奖的卡片个数
function createDiv() {
let parentElement = document.getElementById("lottery-container") as any;
parentElement.innerHTML = props.priceList
.map((v, index) => {
return `<div class = "lottery-con-item">
<div class = "lottery-item-box front"></div>
<div class = "lottery-item-box back"></div>
</div>`;
})
.join("");
let childElement = document.getElementsByClassName("lottery-con-item") as any;
let allElement = document.getElementById("lottery-container")?.children as any;
for (let i = 0; i < allElement.length; i++) {
allElement[i].style.width = Number(props.priceParameter.priceWidth) / 37.5 + "rem";
allElement[i].style.height = Number(props.priceParameter.priceHeight) / 37.5 + "rem";
}
for (let i = 0; i < drawOrder.value.length; i++) {
childElement[drawOrder.value[i]].children[0].style.backgroundImage = `url(${props.priceList[i].front})`;
childElement[drawOrder.value[i]].children[1].style.backgroundImage = `url(${props.priceList[i].back})`;
}
let childHeight = childElement[0].offsetHeight + 15;
parentElement.style.height = `${(2 * childHeight) / 37.5}rem`;
}
// 自定义排序
function customOrder() {
let listArr: any = [];
props.priceList.forEach((val, index) => {
listArr.push(index);
});
// 自定义排序函数
listArr.sort((a: number, b: number) => {
// 定义目标顺序数组
const order = [9, 10, 11, 6, 7, 8, 3, 4, 5, 0, 1, 2];
// 获取元素在目标顺序数组中的索引
const indexA = order.indexOf(a);
const indexB = order.indexOf(b);
// 如果元素不在目标顺序数组中,放在后面
if (indexA === -1) return 1;
if (indexB === -1) return -1;
// 根据目标顺序数组的索引进行排序
return indexA - indexB;
});
drawOrder.value = listArr;
}
// 进行抽奖
const conductLottery = () => {
var allChild = document.getElementsByClassName("lottery-con-item");
const timer = setInterval(() => {
currentIndex.value = drawOrder.value[count.value % drawOrder.value.length];
count.value++;
for (let i = 0; i < allChild.length; i++) {
if (i == currentIndex.value) {
allChild[currentIndex.value].classList.add("lottery-con-item-acitive");
} else {
allChild[i].classList.remove("lottery-con-item-acitive");
}
}
if (count.value > circle.value && currentIndex.value === drawOrder.value[lotteryId.value - 1]) {
// 抽奖结束
clearInterval(timer);
// 停顿一会显示中奖
setTimeout(() => {
count.value = 0;
currentIndex.value = null;
let turnBoxChildren = document.getElementsByClassName(`lottery-con-item-acitive`);
turnBoxChildren[0].children[0].classList.add("lottery-animation1");
turnBoxChildren[0].children[1].classList.add("lottery-animation2");
}, props.priceParameter.intervalTime);
}
}, props.priceParameter.delayTime);
};
</script>
<style>
.lottery-container {
display: flex;
justify-content: space-evenly;
flex-wrap: wrap-reverse;
width: 100%;
min-height: 400px;
box-sizing: border-box;
/* background-color: red; */
}
.lottery-con-item {
position: relative;
transform-style: preserve-3d;
}
.lottery-con-item-acitive {
transform: scale(1.1);
}
.lottery-item-box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: 100% 100%;
backface-visibility: hidden;
}
.lottery-item-box.fornt {
width: 100%;
height: 100%;
animation: backface 1s ease-in-out 0.5s normal forwards;
}
.lottery-item-box.back {
width: 100%;
height: 100%;
backface-visibility: hidden;
}
.lottery-animation1 {
transform: rotateY(180deg);
animation: lottery-backface 1s ease-in-out normal forwards;
}
.lottery-animation2 {
animation: lottery-backface1 1s ease-in-out normal forwards;
}
@keyframes lottery-backface {
100% {
transform: rotateY(0);
}
}
@keyframes lottery-backface1 {
100% {
transform: rotateY(180deg);
}
}
</style>